diff options
-rw-r--r-- | drivers/firmware/qemu_fw_cfg.c | 53 |
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; | |||
45 | static void __iomem *fw_cfg_dev_base; | 46 | static void __iomem *fw_cfg_dev_base; |
46 | static void __iomem *fw_cfg_reg_ctrl; | 47 | static void __iomem *fw_cfg_reg_ctrl; |
47 | static void __iomem *fw_cfg_reg_data; | 48 | static void __iomem *fw_cfg_reg_data; |
49 | static 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) */ |
50 | static DEFINE_MUTEX(fw_cfg_dev_lock); | 52 | static 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) | |||
119 | static int fw_cfg_do_platform_probe(struct platform_device *pdev) | 123 | static 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 | |||
642 | static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp) | 658 | static 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 */ |