aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/firmware/qemu_fw_cfg.c53
1 files changed, 41 insertions, 12 deletions
diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
index 5de6bb406fb6..df028faa2d00 100644
--- a/drivers/firmware/qemu_fw_cfg.c
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -10,20 +10,21 @@
10 * and select subsets of aarch64), a Device Tree node (on arm), or using 10 * and select subsets of aarch64), a Device Tree node (on arm), or using
11 * a kernel module (or command line) parameter with the following syntax: 11 * a kernel module (or command line) parameter with the following syntax:
12 * 12 *
13 * [qemu_fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>] 13 * [qemu_fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]
14 * or 14 * or
15 * [qemu_fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>] 15 * [qemu_fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]
16 * 16 *
17 * where: 17 * where:
18 * <size> := size of ioport or mmio range 18 * <size> := size of ioport or mmio range
19 * <base> := physical base address of ioport or mmio range 19 * <base> := physical base address of ioport or mmio range
20 * <ctrl_off> := (optional) offset of control register 20 * <ctrl_off> := (optional) offset of control register
21 * <data_off> := (optional) offset of data register 21 * <data_off> := (optional) offset of data register
22 * <dma_off> := (optional) offset of dma register
22 * 23 *
23 * e.g.: 24 * e.g.:
24 * qemu_fw_cfg.ioport=2@0x510:0:1 (the default on x86) 25 * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86)
25 * or 26 * or
26 * qemu_fw_cfg.mmio=0xA@0x9020000:8:0 (the default on arm) 27 * qemu_fw_cfg.mmio=16@0x9020000:8:0:16 (the default on arm)
27 */ 28 */
28 29
29#include <linux/module.h> 30#include <linux/module.h>
@@ -45,6 +46,7 @@ static resource_size_t fw_cfg_p_size;
45static void __iomem *fw_cfg_dev_base; 46static void __iomem *fw_cfg_dev_base;
46static void __iomem *fw_cfg_reg_ctrl; 47static void __iomem *fw_cfg_reg_ctrl;
47static void __iomem *fw_cfg_reg_data; 48static void __iomem *fw_cfg_reg_data;
49static void __iomem *fw_cfg_reg_dma;
48 50
49/* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */ 51/* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */
50static DEFINE_MUTEX(fw_cfg_dev_lock); 52static DEFINE_MUTEX(fw_cfg_dev_lock);
@@ -104,12 +106,14 @@ static void fw_cfg_io_cleanup(void)
104# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64)) 106# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64))
105# define FW_CFG_CTRL_OFF 0x08 107# define FW_CFG_CTRL_OFF 0x08
106# define FW_CFG_DATA_OFF 0x00 108# define FW_CFG_DATA_OFF 0x00
109# define FW_CFG_DMA_OFF 0x10
107# elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */ 110# elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */
108# define FW_CFG_CTRL_OFF 0x00 111# define FW_CFG_CTRL_OFF 0x00
109# define FW_CFG_DATA_OFF 0x02 112# define FW_CFG_DATA_OFF 0x02
110# elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */ 113# elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */
111# define FW_CFG_CTRL_OFF 0x00 114# define FW_CFG_CTRL_OFF 0x00
112# define FW_CFG_DATA_OFF 0x01 115# define FW_CFG_DATA_OFF 0x01
116# define FW_CFG_DMA_OFF 0x04
113# else 117# else
114# error "QEMU FW_CFG not available on this architecture!" 118# error "QEMU FW_CFG not available on this architecture!"
115# endif 119# endif
@@ -119,7 +123,7 @@ static void fw_cfg_io_cleanup(void)
119static int fw_cfg_do_platform_probe(struct platform_device *pdev) 123static int fw_cfg_do_platform_probe(struct platform_device *pdev)
120{ 124{
121 char sig[FW_CFG_SIG_SIZE]; 125 char sig[FW_CFG_SIG_SIZE];
122 struct resource *range, *ctrl, *data; 126 struct resource *range, *ctrl, *data, *dma;
123 127
124 /* acquire i/o range details */ 128 /* acquire i/o range details */
125 fw_cfg_is_mmio = false; 129 fw_cfg_is_mmio = false;
@@ -156,6 +160,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev)
156 /* were custom register offsets provided (e.g. on the command line)? */ 160 /* were custom register offsets provided (e.g. on the command line)? */
157 ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl"); 161 ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl");
158 data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data"); 162 data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data");
163 dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma");
159 if (ctrl && data) { 164 if (ctrl && data) {
160 fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start; 165 fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start;
161 fw_cfg_reg_data = fw_cfg_dev_base + data->start; 166 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)
165 fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF; 170 fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF;
166 } 171 }
167 172
173 if (dma)
174 fw_cfg_reg_dma = fw_cfg_dev_base + dma->start;
175#ifdef FW_CFG_DMA_OFF
176 else
177 fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF;
178#endif
179
168 /* verify fw_cfg device signature */ 180 /* verify fw_cfg device signature */
169 if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 181 if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig,
170 0, FW_CFG_SIG_SIZE) < 0 || 182 0, FW_CFG_SIG_SIZE) < 0 ||
@@ -630,6 +642,7 @@ static struct platform_device *fw_cfg_cmdline_dev;
630/* use special scanf/printf modifier for phys_addr_t, resource_size_t */ 642/* use special scanf/printf modifier for phys_addr_t, resource_size_t */
631#define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \ 643#define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \
632 ":%" __PHYS_ADDR_PREFIX "i" \ 644 ":%" __PHYS_ADDR_PREFIX "i" \
645 ":%" __PHYS_ADDR_PREFIX "i%n" \
633 ":%" __PHYS_ADDR_PREFIX "i%n" 646 ":%" __PHYS_ADDR_PREFIX "i%n"
634 647
635#define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \ 648#define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \
@@ -639,12 +652,15 @@ static struct platform_device *fw_cfg_cmdline_dev;
639 ":%" __PHYS_ADDR_PREFIX "u" \ 652 ":%" __PHYS_ADDR_PREFIX "u" \
640 ":%" __PHYS_ADDR_PREFIX "u" 653 ":%" __PHYS_ADDR_PREFIX "u"
641 654
655#define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \
656 ":%" __PHYS_ADDR_PREFIX "u"
657
642static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp) 658static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
643{ 659{
644 struct resource res[3] = {}; 660 struct resource res[4] = {};
645 char *str; 661 char *str;
646 phys_addr_t base; 662 phys_addr_t base;
647 resource_size_t size, ctrl_off, data_off; 663 resource_size_t size, ctrl_off, data_off, dma_off;
648 int processed, consumed = 0; 664 int processed, consumed = 0;
649 665
650 /* only one fw_cfg device can exist system-wide, so if one 666 /* 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)
660 /* consume "<size>" portion of command line argument */ 676 /* consume "<size>" portion of command line argument */
661 size = memparse(arg, &str); 677 size = memparse(arg, &str);
662 678
663 /* get "@<base>[:<ctrl_off>:<data_off>]" chunks */ 679 /* get "@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]" chunks */
664 processed = sscanf(str, PH_ADDR_SCAN_FMT, 680 processed = sscanf(str, PH_ADDR_SCAN_FMT,
665 &base, &consumed, 681 &base, &consumed,
666 &ctrl_off, &data_off, &consumed); 682 &ctrl_off, &data_off, &consumed,
683 &dma_off, &consumed);
667 684
668 /* sscanf() must process precisely 1 or 3 chunks: 685 /* sscanf() must process precisely 1, 3 or 4 chunks:
669 * <base> is mandatory, optionally followed by <ctrl_off> 686 * <base> is mandatory, optionally followed by <ctrl_off>
670 * and <data_off>; 687 * and <data_off>, and <dma_off>;
671 * there must be no extra characters after the last chunk, 688 * there must be no extra characters after the last chunk,
672 * so str[consumed] must be '\0'. 689 * so str[consumed] must be '\0'.
673 */ 690 */
674 if (str[consumed] || 691 if (str[consumed] ||
675 (processed != 1 && processed != 3)) 692 (processed != 1 && processed != 3 && processed != 4))
676 return -EINVAL; 693 return -EINVAL;
677 694
678 res[0].start = base; 695 res[0].start = base;
@@ -689,6 +706,11 @@ static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
689 res[2].start = data_off; 706 res[2].start = data_off;
690 res[2].flags = IORESOURCE_REG; 707 res[2].flags = IORESOURCE_REG;
691 } 708 }
709 if (processed > 3) {
710 res[3].name = "dma";
711 res[3].start = dma_off;
712 res[3].flags = IORESOURCE_REG;
713 }
692 714
693 /* "processed" happens to nicely match the number of resources 715 /* "processed" happens to nicely match the number of resources
694 * we need to pass in to this platform device. 716 * 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)
721 fw_cfg_cmdline_dev->resource[0].start, 743 fw_cfg_cmdline_dev->resource[0].start,
722 fw_cfg_cmdline_dev->resource[1].start, 744 fw_cfg_cmdline_dev->resource[1].start,
723 fw_cfg_cmdline_dev->resource[2].start); 745 fw_cfg_cmdline_dev->resource[2].start);
746 case 4:
747 return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_4_FMT,
748 resource_size(&fw_cfg_cmdline_dev->resource[0]),
749 fw_cfg_cmdline_dev->resource[0].start,
750 fw_cfg_cmdline_dev->resource[1].start,
751 fw_cfg_cmdline_dev->resource[2].start,
752 fw_cfg_cmdline_dev->resource[3].start);
724 } 753 }
725 754
726 /* Should never get here */ 755 /* Should never get here */