Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 14d1824c authored by Marc-André Lureau's avatar Marc-André Lureau Committed by Michael S. Tsirkin
Browse files

fw_cfg: add DMA register



Add an optional <dma_off> kernel module (or command line) parameter
using the following syntax:

      [qemu_fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]
 or
      [qemu_fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]

and initializes the register address using given or default offset.

Signed-off-by: default avatarMarc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: default avatarGabriel Somlo <somlo@cmu.edu>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent 1f57bc12
Loading
Loading
Loading
Loading
+41 −12
Original line number Original line Diff line number Diff line
@@ -10,20 +10,21 @@
 * and select subsets of aarch64), a Device Tree node (on arm), or using
 * and select subsets of aarch64), a Device Tree node (on arm), or using
 * a kernel module (or command line) parameter with the following syntax:
 * a kernel module (or command line) parameter with the following syntax:
 *
 *
 *      [qemu_fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>]
 *      [qemu_fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]
 * or
 * or
 *      [qemu_fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>]
 *      [qemu_fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]
 *
 *
 * where:
 * where:
 *      <size>     := size of ioport or mmio range
 *      <size>     := size of ioport or mmio range
 *      <base>     := physical base address of ioport or mmio range
 *      <base>     := physical base address of ioport or mmio range
 *      <ctrl_off> := (optional) offset of control register
 *      <ctrl_off> := (optional) offset of control register
 *      <data_off> := (optional) offset of data register
 *      <data_off> := (optional) offset of data register
 *      <dma_off> := (optional) offset of dma register
 *
 *
 * e.g.:
 * e.g.:
 *      qemu_fw_cfg.ioport=2@0x510:0:1		(the default on x86)
 *      qemu_fw_cfg.ioport=12@0x510:0:1:4	(the default on x86)
 * or
 * or
 *      qemu_fw_cfg.mmio=0xA@0x9020000:8:0	(the default on arm)
 *      qemu_fw_cfg.mmio=16@0x9020000:8:0:16	(the default on arm)
 */
 */


#include <linux/module.h>
#include <linux/module.h>
@@ -45,6 +46,7 @@ static resource_size_t fw_cfg_p_size;
static void __iomem *fw_cfg_dev_base;
static void __iomem *fw_cfg_dev_base;
static void __iomem *fw_cfg_reg_ctrl;
static void __iomem *fw_cfg_reg_ctrl;
static void __iomem *fw_cfg_reg_data;
static void __iomem *fw_cfg_reg_data;
static void __iomem *fw_cfg_reg_dma;


/* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */
/* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */
static DEFINE_MUTEX(fw_cfg_dev_lock);
static DEFINE_MUTEX(fw_cfg_dev_lock);
@@ -104,12 +106,14 @@ static void fw_cfg_io_cleanup(void)
# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64))
# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64))
#  define FW_CFG_CTRL_OFF 0x08
#  define FW_CFG_CTRL_OFF 0x08
#  define FW_CFG_DATA_OFF 0x00
#  define FW_CFG_DATA_OFF 0x00
#  define FW_CFG_DMA_OFF 0x10
# elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */
# elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */
#  define FW_CFG_CTRL_OFF 0x00
#  define FW_CFG_CTRL_OFF 0x00
#  define FW_CFG_DATA_OFF 0x02
#  define FW_CFG_DATA_OFF 0x02
# elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */
# elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */
#  define FW_CFG_CTRL_OFF 0x00
#  define FW_CFG_CTRL_OFF 0x00
#  define FW_CFG_DATA_OFF 0x01
#  define FW_CFG_DATA_OFF 0x01
#  define FW_CFG_DMA_OFF 0x04
# else
# else
#  error "QEMU FW_CFG not available on this architecture!"
#  error "QEMU FW_CFG not available on this architecture!"
# endif
# endif
@@ -119,7 +123,7 @@ static void fw_cfg_io_cleanup(void)
static int fw_cfg_do_platform_probe(struct platform_device *pdev)
static int fw_cfg_do_platform_probe(struct platform_device *pdev)
{
{
	char sig[FW_CFG_SIG_SIZE];
	char sig[FW_CFG_SIG_SIZE];
	struct resource *range, *ctrl, *data;
	struct resource *range, *ctrl, *data, *dma;


	/* acquire i/o range details */
	/* acquire i/o range details */
	fw_cfg_is_mmio = false;
	fw_cfg_is_mmio = false;
@@ -156,6 +160,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev)
	/* were custom register offsets provided (e.g. on the command line)? */
	/* were custom register offsets provided (e.g. on the command line)? */
	ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl");
	ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl");
	data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data");
	data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data");
	dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma");
	if (ctrl && data) {
	if (ctrl && data) {
		fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start;
		fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start;
		fw_cfg_reg_data = fw_cfg_dev_base + data->start;
		fw_cfg_reg_data = fw_cfg_dev_base + data->start;
@@ -165,6 +170,13 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev)
		fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF;
		fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF;
	}
	}


	if (dma)
		fw_cfg_reg_dma = fw_cfg_dev_base + dma->start;
#ifdef FW_CFG_DMA_OFF
	else
		fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF;
#endif

	/* verify fw_cfg device signature */
	/* verify fw_cfg device signature */
	if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig,
	if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig,
				0, FW_CFG_SIG_SIZE) < 0 ||
				0, FW_CFG_SIG_SIZE) < 0 ||
@@ -630,6 +642,7 @@ static struct platform_device *fw_cfg_cmdline_dev;
/* use special scanf/printf modifier for phys_addr_t, resource_size_t */
/* use special scanf/printf modifier for phys_addr_t, resource_size_t */
#define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \
#define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \
			 ":%" __PHYS_ADDR_PREFIX "i" \
			 ":%" __PHYS_ADDR_PREFIX "i" \
			 ":%" __PHYS_ADDR_PREFIX "i%n" \
			 ":%" __PHYS_ADDR_PREFIX "i%n"
			 ":%" __PHYS_ADDR_PREFIX "i%n"


#define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \
#define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \
@@ -639,12 +652,15 @@ static struct platform_device *fw_cfg_cmdline_dev;
			 ":%" __PHYS_ADDR_PREFIX "u" \
			 ":%" __PHYS_ADDR_PREFIX "u" \
			 ":%" __PHYS_ADDR_PREFIX "u"
			 ":%" __PHYS_ADDR_PREFIX "u"


#define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \
			 ":%" __PHYS_ADDR_PREFIX "u"

static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
{
{
	struct resource res[3] = {};
	struct resource res[4] = {};
	char *str;
	char *str;
	phys_addr_t base;
	phys_addr_t base;
	resource_size_t size, ctrl_off, data_off;
	resource_size_t size, ctrl_off, data_off, dma_off;
	int processed, consumed = 0;
	int processed, consumed = 0;


	/* only one fw_cfg device can exist system-wide, so if one
	/* only one fw_cfg device can exist system-wide, so if one
@@ -660,19 +676,20 @@ static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
	/* consume "<size>" portion of command line argument */
	/* consume "<size>" portion of command line argument */
	size = memparse(arg, &str);
	size = memparse(arg, &str);


	/* get "@<base>[:<ctrl_off>:<data_off>]" chunks */
	/* get "@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]" chunks */
	processed = sscanf(str, PH_ADDR_SCAN_FMT,
	processed = sscanf(str, PH_ADDR_SCAN_FMT,
			   &base, &consumed,
			   &base, &consumed,
			   &ctrl_off, &data_off, &consumed);
			   &ctrl_off, &data_off, &consumed,
			   &dma_off, &consumed);


	/* sscanf() must process precisely 1 or 3 chunks:
	/* sscanf() must process precisely 1, 3 or 4 chunks:
	 * <base> is mandatory, optionally followed by <ctrl_off>
	 * <base> is mandatory, optionally followed by <ctrl_off>
	 * and <data_off>;
	 * and <data_off>, and <dma_off>;
	 * there must be no extra characters after the last chunk,
	 * there must be no extra characters after the last chunk,
	 * so str[consumed] must be '\0'.
	 * so str[consumed] must be '\0'.
	 */
	 */
	if (str[consumed] ||
	if (str[consumed] ||
	    (processed != 1 && processed != 3))
	    (processed != 1 && processed != 3 && processed != 4))
		return -EINVAL;
		return -EINVAL;


	res[0].start = base;
	res[0].start = base;
@@ -689,6 +706,11 @@ static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
		res[2].start = data_off;
		res[2].start = data_off;
		res[2].flags = IORESOURCE_REG;
		res[2].flags = IORESOURCE_REG;
	}
	}
	if (processed > 3) {
		res[3].name = "dma";
		res[3].start = dma_off;
		res[3].flags = IORESOURCE_REG;
	}


	/* "processed" happens to nicely match the number of resources
	/* "processed" happens to nicely match the number of resources
	 * we need to pass in to this platform device.
	 * we need to pass in to this platform device.
@@ -721,6 +743,13 @@ static int fw_cfg_cmdline_get(char *buf, const struct kernel_param *kp)
				fw_cfg_cmdline_dev->resource[0].start,
				fw_cfg_cmdline_dev->resource[0].start,
				fw_cfg_cmdline_dev->resource[1].start,
				fw_cfg_cmdline_dev->resource[1].start,
				fw_cfg_cmdline_dev->resource[2].start);
				fw_cfg_cmdline_dev->resource[2].start);
	case 4:
		return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_4_FMT,
				resource_size(&fw_cfg_cmdline_dev->resource[0]),
				fw_cfg_cmdline_dev->resource[0].start,
				fw_cfg_cmdline_dev->resource[1].start,
				fw_cfg_cmdline_dev->resource[2].start,
				fw_cfg_cmdline_dev->resource[3].start);
	}
	}


	/* Should never get here */
	/* Should never get here */