/* SPDX-License-Identifier: GPL-2.0-or-later */

#ifndef ATA_DISK_H
#define ATA_DISK_H

#include <block.h>

/* IDE register file */
#define IDE_REG_DATA 0x00
#define IDE_REG_ERR 0x01
#define IDE_REG_NSECT 0x02
#define IDE_REG_LBAL 0x03
#define IDE_REG_LBAM 0x04
#define IDE_REG_LBAH 0x05
#define IDE_REG_DEVICE 0x06
#define IDE_REG_STATUS 0x07

#define IDE_REG_FEATURE IDE_REG_ERR /* and their aliases */
#define IDE_REG_CMD IDE_REG_STATUS

#define IDE_REG_ALT_STATUS 0x00
#define IDE_REG_DEV_CTL 0x00
#define IDE_REG_DRV_ADDR 0x01

#define ATA_CMD_ID_ATA		0xEC
#define ATA_CMD_READ		0x20
#define ATA_CMD_PIO_READ_EXT	0x24
#define ATA_CMD_READ_EXT	0x25
#define ATA_CMD_WRITE		0x30
#define ATA_CMD_PIO_WRITE_EXT	0x34
#define ATA_CMD_WRITE_EXT	0x35

/* drive's status flags */
#define ATA_STATUS_BUSY		(1 << 7)
#define ATA_STATUS_READY	(1 << 6)
#define ATA_STATUS_WR_FLT	(1 << 5)
#define ATA_STATUS_DSC		(1 << 4)
#define ATA_STATUS_DRQ		(1 << 3)
#define ATA_STATUS_CORR		(1 << 2)
#define ATA_STATUS_IDX		(1 << 1)
#define ATA_STATUS_ERROR	(1 << 0)
/* command flags */
#define LBA_FLAG		(1 << 6)
#define ATA_DEVCTL_SOFT_RESET	(1 << 2)
#define ATA_DEVCTL_INTR_DISABLE	(1 << 1)

#define ata_id_u32(id,n)        \
        (((uint32_t) (id)[(n) + 1] << 16) | ((uint32_t) (id)[(n)]))
#define ata_id_u64(id,n)        \
        ( ((uint64_t) (id)[(n) + 3] << 48) | \
          ((uint64_t) (id)[(n) + 2] << 32) | \
          ((uint64_t) (id)[(n) + 1] << 16) | \
          ((uint64_t) (id)[(n) + 0]) )

#define ata_id_has_lba(id)               ((id)[49] & (1 << 9))

enum {
	ATA_ID_SERNO		= 10,
#define ATA_ID_SERNO_LEN 20
	ATA_ID_FW_REV		= 23,
#define ATA_ID_FW_REV_LEN 8
	ATA_ID_PROD		= 27,
#define ATA_ID_PROD_LEN 40
	ATA_ID_CAPABILITY	= 49,
	ATA_ID_FIELD_VALID	= 53,
	ATA_ID_LBA_CAPACITY	= 60,
	ATA_ID_MWDMA_MODES	= 63,
	ATA_ID_PIO_MODES	= 64,
	ATA_ID_QUEUE_DEPTH	= 75,
	ATA_ID_MAJOR_VER	= 80,
	ATA_ID_COMMAND_SET_1	= 82,
	ATA_ID_COMMAND_SET_2	= 83,
	ATA_ID_CFSSE		= 84,
	ATA_ID_CFS_ENABLE_1	= 85,
	ATA_ID_CFS_ENABLE_2	= 86,
	ATA_ID_CSF_DEFAULT	= 87,
	ATA_ID_UDMA_MODES	= 88,
	ATA_ID_HW_CONFIG	= 93,
	ATA_ID_LBA_CAPACITY_2	= 100,
};

static inline int ata_id_has_lba48(const uint16_t *id)
{
	if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000)
		return 0;
	if (!ata_id_u64(id, ATA_ID_LBA_CAPACITY_2))
		return 0;
	return id[ATA_ID_COMMAND_SET_2] & (1 << 10);
}

/** addresses of each individual IDE drive register */
struct ata_ioports {
	void __iomem *cmd_addr;
	void __iomem *data_addr;
	void __iomem *error_addr;
	void __iomem *feature_addr;
	void __iomem *nsect_addr;
	void __iomem *lbal_addr;
	void __iomem *lbam_addr;
	void __iomem *lbah_addr;
	void __iomem *device_addr;
	void __iomem *status_addr;
	void __iomem *command_addr;
	void __iomem *altstatus_addr;
	void __iomem *ctl_addr;
	void __iomem *alt_dev_addr;

	/* hard reset line handling */
	void (*reset)(int);	/* true: assert reset, false: de-assert reset */
	int dataif_be;	/* true if 16 bit data register is big endian */
	int mmio; /* true if memory-mapped io */
};

struct ata_port;

struct ata_port_operations {
	int (*init)(struct ata_port *port);
	int (*read)(struct ata_port *port, void *buf, sector_t block, blkcnt_t num_blocks);
	int (*write)(struct ata_port *port, const void *buf, sector_t block, blkcnt_t num_blocks);
	int (*read_id)(struct ata_port *port, void *buf);
	int (*reset)(struct ata_port *port);
};

struct ata_port {
	struct ata_port_operations *ops;
	struct device_d *dev;
	struct device_d class_dev;
	const char *devname;
	void *drvdata;
	struct block_device blk;
	uint16_t *id;
	int lba48;
	int initialized;
	int probe;
};

struct ide_port {
	struct ata_ioports io;	/**< register file */
	struct ata_port port;
};

int ide_port_register(struct ide_port *ide);
int ata_port_register(struct ata_port *port);
int ata_port_detect(struct ata_port *port);

struct device_d;

/**
 * @file
 * @brief Register file examples of generic types of ATA devices
 *
 * PC IDE:
 *
 *        Offset       Read      Write           Note
 *-----------------------------------------------------------
 *        0x1f0        data      data        16 bit register
 *        0x1f1        error    feature
 *        0x1f2       sec cnt   set cnt
 *        0x1f3       sec no    sec no
 *        0x1f4       cyl low   cyl low
 *        0x1f5       cyl high  cyl high
 *        0x1f6        head      head
 *        0x1f7       status    command
 *        0x3f6     alt status  dev cntrl
 *        0x3f7       drv addr
 *
 * PCMCIA memory mapped:
 *
 *        Offset       Read      Write           Note
 *-----------------------------------------------------------
 *        0x0          data      data        16 bit register
 *        0x1          error    feature
 *        0x2         sec cnt   set cnt
 *        0x3         sec no    sec no
 *        0x4         cyl low   cyl low
 *        0x5         cyl high  cyl high
 *        0x6          head      head
 *        0x7         status    command
 *        0x8          data      data       16 bit or 8 bit register (even byte)
 *        0x9          data      data       8 bit register (odd byte)
 *        0xd          error    feature     dup of offset 1
 *        0xe       alt status  dev cntrl
 *        0xf        drv addr
 *       0x400         data      data       16 bit area with 1 kiB in size
 */

#endif /* ATA_DISK_H */
