// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (c) 2014 Google, Inc
 * Written by Simon Glass <sjg@chromium.org>
 */

/*
 * IO space access commands.
 */

#define LOG_CATEGORY	UCLASS_PCI

#include <command.h>
#include <dm.h>
#include <log.h>
#include <asm/io.h>

int pci_map_physmem(phys_addr_t paddr, unsigned long *lenp,
		    struct udevice **devp, void **ptrp)
{
	struct udevice *dev;
	int ret;

	*ptrp = 0;
	for (uclass_first_device(UCLASS_PCI_EMUL, &dev);
	     dev;
	     uclass_next_device(&dev)) {
		struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev);

		if (!ops || !ops->map_physmem)
			continue;
		ret = (ops->map_physmem)(dev, paddr, lenp, ptrp);
		if (ret)
			continue;
		*devp = dev;
		log_debug("addr=%lx, dev=%s\n", (ulong)paddr, dev->name);
		return 0;
	}

	log_debug("%s: failed: addr=%pap\n", __func__, &paddr);
	return -ENOSYS;
}

int pci_unmap_physmem(const void *vaddr, unsigned long len,
		      struct udevice *dev)
{
	struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev);

	if (!ops || !ops->unmap_physmem)
		return -ENOSYS;
	return (ops->unmap_physmem)(dev, vaddr, len);
}

static int pci_io_read(unsigned int addr, ulong *valuep, pci_size_t size)
{
	struct udevice *dev;
	int ret;

	*valuep = pci_get_ff(size);
	for (uclass_first_device(UCLASS_PCI_EMUL, &dev);
	     dev;
	     uclass_next_device(&dev)) {
		struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev);

		if (ops && ops->read_io) {
			ret = (ops->read_io)(dev, addr, valuep, size);
			if (!ret)
				return 0;
		}
	}

	log_debug("%s: failed: addr=%x\n", __func__, addr);
	return -ENOSYS;
}

static int pci_io_write(unsigned int addr, ulong value, pci_size_t size)
{
	struct udevice *dev;
	int ret;

	for (uclass_first_device(UCLASS_PCI_EMUL, &dev);
	     dev;
	     uclass_next_device(&dev)) {
		struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev);

		if (ops && ops->write_io) {
			ret = (ops->write_io)(dev, addr, value, size);
			if (!ret)
				return 0;
		}
	}

	log_debug("%s: failed: addr=%x, value=%lx\n", __func__, addr, value);
	return -ENOSYS;
}

int _inl(unsigned int addr)
{
	unsigned long value;
	int ret;

	ret = pci_io_read(addr, &value, PCI_SIZE_32);

	return ret ? 0 : value;
}

int _inw(unsigned int addr)
{
	unsigned long value;
	int ret;

	ret = pci_io_read(addr, &value, PCI_SIZE_16);

	return ret ? 0 : value;
}

int _inb(unsigned int addr)
{
	unsigned long value;
	int ret;

	ret = pci_io_read(addr, &value, PCI_SIZE_8);

	return ret ? 0 : value;
}

void _outl(unsigned int value, unsigned int addr)
{
	pci_io_write(addr, value, PCI_SIZE_32);
}

void _outw(unsigned int value, unsigned int addr)
{
	pci_io_write(addr, value, PCI_SIZE_16);
}

void _outb(unsigned int value, unsigned int addr)
{
	pci_io_write(addr, value, PCI_SIZE_8);
}
