xref: /openbmc/linux/drivers/firmware/qemu_fw_cfg.c (revision 75f3e8e47f381074801d0034874d20c638d9e3d9)
1*75f3e8e4SGabriel Somlo /*
2*75f3e8e4SGabriel Somlo  * drivers/firmware/qemu_fw_cfg.c
3*75f3e8e4SGabriel Somlo  *
4*75f3e8e4SGabriel Somlo  * Copyright 2015 Carnegie Mellon University
5*75f3e8e4SGabriel Somlo  *
6*75f3e8e4SGabriel Somlo  * Expose entries from QEMU's firmware configuration (fw_cfg) device in
7*75f3e8e4SGabriel Somlo  * sysfs (read-only, under "/sys/firmware/qemu_fw_cfg/...").
8*75f3e8e4SGabriel Somlo  *
9*75f3e8e4SGabriel Somlo  * The fw_cfg device may be instantiated via either an ACPI node (on x86
10*75f3e8e4SGabriel Somlo  * and select subsets of aarch64), a Device Tree node (on arm), or using
11*75f3e8e4SGabriel Somlo  * a kernel module (or command line) parameter with the following syntax:
12*75f3e8e4SGabriel Somlo  *
13*75f3e8e4SGabriel Somlo  *      [fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>]
14*75f3e8e4SGabriel Somlo  * or
15*75f3e8e4SGabriel Somlo  *      [fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>]
16*75f3e8e4SGabriel Somlo  *
17*75f3e8e4SGabriel Somlo  * where:
18*75f3e8e4SGabriel Somlo  *      <size>     := size of ioport or mmio range
19*75f3e8e4SGabriel Somlo  *      <base>     := physical base address of ioport or mmio range
20*75f3e8e4SGabriel Somlo  *      <ctrl_off> := (optional) offset of control register
21*75f3e8e4SGabriel Somlo  *      <data_off> := (optional) offset of data register
22*75f3e8e4SGabriel Somlo  *
23*75f3e8e4SGabriel Somlo  * e.g.:
24*75f3e8e4SGabriel Somlo  *      fw_cfg.ioport=2@0x510:0:1		(the default on x86)
25*75f3e8e4SGabriel Somlo  * or
26*75f3e8e4SGabriel Somlo  *      fw_cfg.mmio=0xA@0x9020000:8:0		(the default on arm)
27*75f3e8e4SGabriel Somlo  */
28*75f3e8e4SGabriel Somlo 
29*75f3e8e4SGabriel Somlo #include <linux/module.h>
30*75f3e8e4SGabriel Somlo #include <linux/platform_device.h>
31*75f3e8e4SGabriel Somlo #include <linux/acpi.h>
32*75f3e8e4SGabriel Somlo #include <linux/slab.h>
33*75f3e8e4SGabriel Somlo #include <linux/io.h>
34*75f3e8e4SGabriel Somlo #include <linux/ioport.h>
35*75f3e8e4SGabriel Somlo 
36*75f3e8e4SGabriel Somlo MODULE_AUTHOR("Gabriel L. Somlo <somlo@cmu.edu>");
37*75f3e8e4SGabriel Somlo MODULE_DESCRIPTION("QEMU fw_cfg sysfs support");
38*75f3e8e4SGabriel Somlo MODULE_LICENSE("GPL");
39*75f3e8e4SGabriel Somlo 
40*75f3e8e4SGabriel Somlo /* selector key values for "well-known" fw_cfg entries */
41*75f3e8e4SGabriel Somlo #define FW_CFG_SIGNATURE  0x00
42*75f3e8e4SGabriel Somlo #define FW_CFG_ID         0x01
43*75f3e8e4SGabriel Somlo #define FW_CFG_FILE_DIR   0x19
44*75f3e8e4SGabriel Somlo 
45*75f3e8e4SGabriel Somlo /* size in bytes of fw_cfg signature */
46*75f3e8e4SGabriel Somlo #define FW_CFG_SIG_SIZE 4
47*75f3e8e4SGabriel Somlo 
48*75f3e8e4SGabriel Somlo /* fw_cfg "file name" is up to 56 characters (including terminating nul) */
49*75f3e8e4SGabriel Somlo #define FW_CFG_MAX_FILE_PATH 56
50*75f3e8e4SGabriel Somlo 
51*75f3e8e4SGabriel Somlo /* fw_cfg file directory entry type */
52*75f3e8e4SGabriel Somlo struct fw_cfg_file {
53*75f3e8e4SGabriel Somlo 	u32 size;
54*75f3e8e4SGabriel Somlo 	u16 select;
55*75f3e8e4SGabriel Somlo 	u16 reserved;
56*75f3e8e4SGabriel Somlo 	char name[FW_CFG_MAX_FILE_PATH];
57*75f3e8e4SGabriel Somlo };
58*75f3e8e4SGabriel Somlo 
59*75f3e8e4SGabriel Somlo /* fw_cfg device i/o register addresses */
60*75f3e8e4SGabriel Somlo static bool fw_cfg_is_mmio;
61*75f3e8e4SGabriel Somlo static phys_addr_t fw_cfg_p_base;
62*75f3e8e4SGabriel Somlo static resource_size_t fw_cfg_p_size;
63*75f3e8e4SGabriel Somlo static void __iomem *fw_cfg_dev_base;
64*75f3e8e4SGabriel Somlo static void __iomem *fw_cfg_reg_ctrl;
65*75f3e8e4SGabriel Somlo static void __iomem *fw_cfg_reg_data;
66*75f3e8e4SGabriel Somlo 
67*75f3e8e4SGabriel Somlo /* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */
68*75f3e8e4SGabriel Somlo static DEFINE_MUTEX(fw_cfg_dev_lock);
69*75f3e8e4SGabriel Somlo 
70*75f3e8e4SGabriel Somlo /* pick appropriate endianness for selector key */
71*75f3e8e4SGabriel Somlo static inline u16 fw_cfg_sel_endianness(u16 key)
72*75f3e8e4SGabriel Somlo {
73*75f3e8e4SGabriel Somlo 	return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key);
74*75f3e8e4SGabriel Somlo }
75*75f3e8e4SGabriel Somlo 
76*75f3e8e4SGabriel Somlo /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */
77*75f3e8e4SGabriel Somlo static inline void fw_cfg_read_blob(u16 key,
78*75f3e8e4SGabriel Somlo 				    void *buf, loff_t pos, size_t count)
79*75f3e8e4SGabriel Somlo {
80*75f3e8e4SGabriel Somlo 	mutex_lock(&fw_cfg_dev_lock);
81*75f3e8e4SGabriel Somlo 	iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl);
82*75f3e8e4SGabriel Somlo 	while (pos-- > 0)
83*75f3e8e4SGabriel Somlo 		ioread8(fw_cfg_reg_data);
84*75f3e8e4SGabriel Somlo 	ioread8_rep(fw_cfg_reg_data, buf, count);
85*75f3e8e4SGabriel Somlo 	mutex_unlock(&fw_cfg_dev_lock);
86*75f3e8e4SGabriel Somlo }
87*75f3e8e4SGabriel Somlo 
88*75f3e8e4SGabriel Somlo /* clean up fw_cfg device i/o */
89*75f3e8e4SGabriel Somlo static void fw_cfg_io_cleanup(void)
90*75f3e8e4SGabriel Somlo {
91*75f3e8e4SGabriel Somlo 	if (fw_cfg_is_mmio) {
92*75f3e8e4SGabriel Somlo 		iounmap(fw_cfg_dev_base);
93*75f3e8e4SGabriel Somlo 		release_mem_region(fw_cfg_p_base, fw_cfg_p_size);
94*75f3e8e4SGabriel Somlo 	} else {
95*75f3e8e4SGabriel Somlo 		ioport_unmap(fw_cfg_dev_base);
96*75f3e8e4SGabriel Somlo 		release_region(fw_cfg_p_base, fw_cfg_p_size);
97*75f3e8e4SGabriel Somlo 	}
98*75f3e8e4SGabriel Somlo }
99*75f3e8e4SGabriel Somlo 
100*75f3e8e4SGabriel Somlo /* arch-specific ctrl & data register offsets are not available in ACPI, DT */
101*75f3e8e4SGabriel Somlo #if !(defined(FW_CFG_CTRL_OFF) && defined(FW_CTRL_DATA_OFF))
102*75f3e8e4SGabriel Somlo # if (defined(CONFIG_ARM) || defined(CONFIG_ARM64))
103*75f3e8e4SGabriel Somlo #  define FW_CFG_CTRL_OFF 0x08
104*75f3e8e4SGabriel Somlo #  define FW_CFG_DATA_OFF 0x00
105*75f3e8e4SGabriel Somlo # elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */
106*75f3e8e4SGabriel Somlo #  define FW_CFG_CTRL_OFF 0x00
107*75f3e8e4SGabriel Somlo #  define FW_CFG_DATA_OFF 0x02
108*75f3e8e4SGabriel Somlo # elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */
109*75f3e8e4SGabriel Somlo #  define FW_CFG_CTRL_OFF 0x00
110*75f3e8e4SGabriel Somlo #  define FW_CFG_DATA_OFF 0x01
111*75f3e8e4SGabriel Somlo # else
112*75f3e8e4SGabriel Somlo #  warning "QEMU FW_CFG may not be available on this architecture!"
113*75f3e8e4SGabriel Somlo #  define FW_CFG_CTRL_OFF 0x00
114*75f3e8e4SGabriel Somlo #  define FW_CFG_DATA_OFF 0x01
115*75f3e8e4SGabriel Somlo # endif
116*75f3e8e4SGabriel Somlo #endif
117*75f3e8e4SGabriel Somlo 
118*75f3e8e4SGabriel Somlo /* initialize fw_cfg device i/o from platform data */
119*75f3e8e4SGabriel Somlo static int fw_cfg_do_platform_probe(struct platform_device *pdev)
120*75f3e8e4SGabriel Somlo {
121*75f3e8e4SGabriel Somlo 	char sig[FW_CFG_SIG_SIZE];
122*75f3e8e4SGabriel Somlo 	struct resource *range, *ctrl, *data;
123*75f3e8e4SGabriel Somlo 
124*75f3e8e4SGabriel Somlo 	/* acquire i/o range details */
125*75f3e8e4SGabriel Somlo 	fw_cfg_is_mmio = false;
126*75f3e8e4SGabriel Somlo 	range = platform_get_resource(pdev, IORESOURCE_IO, 0);
127*75f3e8e4SGabriel Somlo 	if (!range) {
128*75f3e8e4SGabriel Somlo 		fw_cfg_is_mmio = true;
129*75f3e8e4SGabriel Somlo 		range = platform_get_resource(pdev, IORESOURCE_MEM, 0);
130*75f3e8e4SGabriel Somlo 		if (!range)
131*75f3e8e4SGabriel Somlo 			return -EINVAL;
132*75f3e8e4SGabriel Somlo 	}
133*75f3e8e4SGabriel Somlo 	fw_cfg_p_base = range->start;
134*75f3e8e4SGabriel Somlo 	fw_cfg_p_size = resource_size(range);
135*75f3e8e4SGabriel Somlo 
136*75f3e8e4SGabriel Somlo 	if (fw_cfg_is_mmio) {
137*75f3e8e4SGabriel Somlo 		if (!request_mem_region(fw_cfg_p_base,
138*75f3e8e4SGabriel Somlo 					fw_cfg_p_size, "fw_cfg_mem"))
139*75f3e8e4SGabriel Somlo 			return -EBUSY;
140*75f3e8e4SGabriel Somlo 		fw_cfg_dev_base = ioremap(fw_cfg_p_base, fw_cfg_p_size);
141*75f3e8e4SGabriel Somlo 		if (!fw_cfg_dev_base) {
142*75f3e8e4SGabriel Somlo 			release_mem_region(fw_cfg_p_base, fw_cfg_p_size);
143*75f3e8e4SGabriel Somlo 			return -EFAULT;
144*75f3e8e4SGabriel Somlo 		}
145*75f3e8e4SGabriel Somlo 	} else {
146*75f3e8e4SGabriel Somlo 		if (!request_region(fw_cfg_p_base,
147*75f3e8e4SGabriel Somlo 				    fw_cfg_p_size, "fw_cfg_io"))
148*75f3e8e4SGabriel Somlo 			return -EBUSY;
149*75f3e8e4SGabriel Somlo 		fw_cfg_dev_base = ioport_map(fw_cfg_p_base, fw_cfg_p_size);
150*75f3e8e4SGabriel Somlo 		if (!fw_cfg_dev_base) {
151*75f3e8e4SGabriel Somlo 			release_region(fw_cfg_p_base, fw_cfg_p_size);
152*75f3e8e4SGabriel Somlo 			return -EFAULT;
153*75f3e8e4SGabriel Somlo 		}
154*75f3e8e4SGabriel Somlo 	}
155*75f3e8e4SGabriel Somlo 
156*75f3e8e4SGabriel Somlo 	/* were custom register offsets provided (e.g. on the command line)? */
157*75f3e8e4SGabriel Somlo 	ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl");
158*75f3e8e4SGabriel Somlo 	data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data");
159*75f3e8e4SGabriel Somlo 	if (ctrl && data) {
160*75f3e8e4SGabriel Somlo 		fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start;
161*75f3e8e4SGabriel Somlo 		fw_cfg_reg_data = fw_cfg_dev_base + data->start;
162*75f3e8e4SGabriel Somlo 	} else {
163*75f3e8e4SGabriel Somlo 		/* use architecture-specific offsets */
164*75f3e8e4SGabriel Somlo 		fw_cfg_reg_ctrl = fw_cfg_dev_base + FW_CFG_CTRL_OFF;
165*75f3e8e4SGabriel Somlo 		fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF;
166*75f3e8e4SGabriel Somlo 	}
167*75f3e8e4SGabriel Somlo 
168*75f3e8e4SGabriel Somlo 	/* verify fw_cfg device signature */
169*75f3e8e4SGabriel Somlo 	fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE);
170*75f3e8e4SGabriel Somlo 	if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) {
171*75f3e8e4SGabriel Somlo 		fw_cfg_io_cleanup();
172*75f3e8e4SGabriel Somlo 		return -ENODEV;
173*75f3e8e4SGabriel Somlo 	}
174*75f3e8e4SGabriel Somlo 
175*75f3e8e4SGabriel Somlo 	return 0;
176*75f3e8e4SGabriel Somlo }
177*75f3e8e4SGabriel Somlo 
178*75f3e8e4SGabriel Somlo /* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */
179*75f3e8e4SGabriel Somlo static u32 fw_cfg_rev;
180*75f3e8e4SGabriel Somlo 
181*75f3e8e4SGabriel Somlo static ssize_t fw_cfg_showrev(struct kobject *k, struct attribute *a, char *buf)
182*75f3e8e4SGabriel Somlo {
183*75f3e8e4SGabriel Somlo 	return sprintf(buf, "%u\n", fw_cfg_rev);
184*75f3e8e4SGabriel Somlo }
185*75f3e8e4SGabriel Somlo 
186*75f3e8e4SGabriel Somlo static const struct {
187*75f3e8e4SGabriel Somlo 	struct attribute attr;
188*75f3e8e4SGabriel Somlo 	ssize_t (*show)(struct kobject *k, struct attribute *a, char *buf);
189*75f3e8e4SGabriel Somlo } fw_cfg_rev_attr = {
190*75f3e8e4SGabriel Somlo 	.attr = { .name = "rev", .mode = S_IRUSR },
191*75f3e8e4SGabriel Somlo 	.show = fw_cfg_showrev,
192*75f3e8e4SGabriel Somlo };
193*75f3e8e4SGabriel Somlo 
194*75f3e8e4SGabriel Somlo /* fw_cfg_sysfs_entry type */
195*75f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry {
196*75f3e8e4SGabriel Somlo 	struct kobject kobj;
197*75f3e8e4SGabriel Somlo 	struct fw_cfg_file f;
198*75f3e8e4SGabriel Somlo 	struct list_head list;
199*75f3e8e4SGabriel Somlo };
200*75f3e8e4SGabriel Somlo 
201*75f3e8e4SGabriel Somlo /* get fw_cfg_sysfs_entry from kobject member */
202*75f3e8e4SGabriel Somlo static inline struct fw_cfg_sysfs_entry *to_entry(struct kobject *kobj)
203*75f3e8e4SGabriel Somlo {
204*75f3e8e4SGabriel Somlo 	return container_of(kobj, struct fw_cfg_sysfs_entry, kobj);
205*75f3e8e4SGabriel Somlo }
206*75f3e8e4SGabriel Somlo 
207*75f3e8e4SGabriel Somlo /* fw_cfg_sysfs_attribute type */
208*75f3e8e4SGabriel Somlo struct fw_cfg_sysfs_attribute {
209*75f3e8e4SGabriel Somlo 	struct attribute attr;
210*75f3e8e4SGabriel Somlo 	ssize_t (*show)(struct fw_cfg_sysfs_entry *entry, char *buf);
211*75f3e8e4SGabriel Somlo };
212*75f3e8e4SGabriel Somlo 
213*75f3e8e4SGabriel Somlo /* get fw_cfg_sysfs_attribute from attribute member */
214*75f3e8e4SGabriel Somlo static inline struct fw_cfg_sysfs_attribute *to_attr(struct attribute *attr)
215*75f3e8e4SGabriel Somlo {
216*75f3e8e4SGabriel Somlo 	return container_of(attr, struct fw_cfg_sysfs_attribute, attr);
217*75f3e8e4SGabriel Somlo }
218*75f3e8e4SGabriel Somlo 
219*75f3e8e4SGabriel Somlo /* global cache of fw_cfg_sysfs_entry objects */
220*75f3e8e4SGabriel Somlo static LIST_HEAD(fw_cfg_entry_cache);
221*75f3e8e4SGabriel Somlo 
222*75f3e8e4SGabriel Somlo /* kobjects removed lazily by kernel, mutual exclusion needed */
223*75f3e8e4SGabriel Somlo static DEFINE_SPINLOCK(fw_cfg_cache_lock);
224*75f3e8e4SGabriel Somlo 
225*75f3e8e4SGabriel Somlo static inline void fw_cfg_sysfs_cache_enlist(struct fw_cfg_sysfs_entry *entry)
226*75f3e8e4SGabriel Somlo {
227*75f3e8e4SGabriel Somlo 	spin_lock(&fw_cfg_cache_lock);
228*75f3e8e4SGabriel Somlo 	list_add_tail(&entry->list, &fw_cfg_entry_cache);
229*75f3e8e4SGabriel Somlo 	spin_unlock(&fw_cfg_cache_lock);
230*75f3e8e4SGabriel Somlo }
231*75f3e8e4SGabriel Somlo 
232*75f3e8e4SGabriel Somlo static inline void fw_cfg_sysfs_cache_delist(struct fw_cfg_sysfs_entry *entry)
233*75f3e8e4SGabriel Somlo {
234*75f3e8e4SGabriel Somlo 	spin_lock(&fw_cfg_cache_lock);
235*75f3e8e4SGabriel Somlo 	list_del(&entry->list);
236*75f3e8e4SGabriel Somlo 	spin_unlock(&fw_cfg_cache_lock);
237*75f3e8e4SGabriel Somlo }
238*75f3e8e4SGabriel Somlo 
239*75f3e8e4SGabriel Somlo static void fw_cfg_sysfs_cache_cleanup(void)
240*75f3e8e4SGabriel Somlo {
241*75f3e8e4SGabriel Somlo 	struct fw_cfg_sysfs_entry *entry, *next;
242*75f3e8e4SGabriel Somlo 
243*75f3e8e4SGabriel Somlo 	list_for_each_entry_safe(entry, next, &fw_cfg_entry_cache, list) {
244*75f3e8e4SGabriel Somlo 		/* will end up invoking fw_cfg_sysfs_cache_delist()
245*75f3e8e4SGabriel Somlo 		 * via each object's release() method (i.e. destructor)
246*75f3e8e4SGabriel Somlo 		 */
247*75f3e8e4SGabriel Somlo 		kobject_put(&entry->kobj);
248*75f3e8e4SGabriel Somlo 	}
249*75f3e8e4SGabriel Somlo }
250*75f3e8e4SGabriel Somlo 
251*75f3e8e4SGabriel Somlo /* default_attrs: per-entry attributes and show methods */
252*75f3e8e4SGabriel Somlo 
253*75f3e8e4SGabriel Somlo #define FW_CFG_SYSFS_ATTR(_attr) \
254*75f3e8e4SGabriel Somlo struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \
255*75f3e8e4SGabriel Somlo 	.attr = { .name = __stringify(_attr), .mode = S_IRUSR }, \
256*75f3e8e4SGabriel Somlo 	.show = fw_cfg_sysfs_show_##_attr, \
257*75f3e8e4SGabriel Somlo }
258*75f3e8e4SGabriel Somlo 
259*75f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf)
260*75f3e8e4SGabriel Somlo {
261*75f3e8e4SGabriel Somlo 	return sprintf(buf, "%u\n", e->f.size);
262*75f3e8e4SGabriel Somlo }
263*75f3e8e4SGabriel Somlo 
264*75f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf)
265*75f3e8e4SGabriel Somlo {
266*75f3e8e4SGabriel Somlo 	return sprintf(buf, "%u\n", e->f.select);
267*75f3e8e4SGabriel Somlo }
268*75f3e8e4SGabriel Somlo 
269*75f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf)
270*75f3e8e4SGabriel Somlo {
271*75f3e8e4SGabriel Somlo 	return sprintf(buf, "%s\n", e->f.name);
272*75f3e8e4SGabriel Somlo }
273*75f3e8e4SGabriel Somlo 
274*75f3e8e4SGabriel Somlo static FW_CFG_SYSFS_ATTR(size);
275*75f3e8e4SGabriel Somlo static FW_CFG_SYSFS_ATTR(key);
276*75f3e8e4SGabriel Somlo static FW_CFG_SYSFS_ATTR(name);
277*75f3e8e4SGabriel Somlo 
278*75f3e8e4SGabriel Somlo static struct attribute *fw_cfg_sysfs_entry_attrs[] = {
279*75f3e8e4SGabriel Somlo 	&fw_cfg_sysfs_attr_size.attr,
280*75f3e8e4SGabriel Somlo 	&fw_cfg_sysfs_attr_key.attr,
281*75f3e8e4SGabriel Somlo 	&fw_cfg_sysfs_attr_name.attr,
282*75f3e8e4SGabriel Somlo 	NULL,
283*75f3e8e4SGabriel Somlo };
284*75f3e8e4SGabriel Somlo 
285*75f3e8e4SGabriel Somlo /* sysfs_ops: find fw_cfg_[entry, attribute] and call appropriate show method */
286*75f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_attr_show(struct kobject *kobj, struct attribute *a,
287*75f3e8e4SGabriel Somlo 				      char *buf)
288*75f3e8e4SGabriel Somlo {
289*75f3e8e4SGabriel Somlo 	struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
290*75f3e8e4SGabriel Somlo 	struct fw_cfg_sysfs_attribute *attr = to_attr(a);
291*75f3e8e4SGabriel Somlo 
292*75f3e8e4SGabriel Somlo 	return attr->show(entry, buf);
293*75f3e8e4SGabriel Somlo }
294*75f3e8e4SGabriel Somlo 
295*75f3e8e4SGabriel Somlo static const struct sysfs_ops fw_cfg_sysfs_attr_ops = {
296*75f3e8e4SGabriel Somlo 	.show = fw_cfg_sysfs_attr_show,
297*75f3e8e4SGabriel Somlo };
298*75f3e8e4SGabriel Somlo 
299*75f3e8e4SGabriel Somlo /* release: destructor, to be called via kobject_put() */
300*75f3e8e4SGabriel Somlo static void fw_cfg_sysfs_release_entry(struct kobject *kobj)
301*75f3e8e4SGabriel Somlo {
302*75f3e8e4SGabriel Somlo 	struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
303*75f3e8e4SGabriel Somlo 
304*75f3e8e4SGabriel Somlo 	fw_cfg_sysfs_cache_delist(entry);
305*75f3e8e4SGabriel Somlo 	kfree(entry);
306*75f3e8e4SGabriel Somlo }
307*75f3e8e4SGabriel Somlo 
308*75f3e8e4SGabriel Somlo /* kobj_type: ties together all properties required to register an entry */
309*75f3e8e4SGabriel Somlo static struct kobj_type fw_cfg_sysfs_entry_ktype = {
310*75f3e8e4SGabriel Somlo 	.default_attrs = fw_cfg_sysfs_entry_attrs,
311*75f3e8e4SGabriel Somlo 	.sysfs_ops = &fw_cfg_sysfs_attr_ops,
312*75f3e8e4SGabriel Somlo 	.release = fw_cfg_sysfs_release_entry,
313*75f3e8e4SGabriel Somlo };
314*75f3e8e4SGabriel Somlo 
315*75f3e8e4SGabriel Somlo /* raw-read method and attribute */
316*75f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj,
317*75f3e8e4SGabriel Somlo 				     struct bin_attribute *bin_attr,
318*75f3e8e4SGabriel Somlo 				     char *buf, loff_t pos, size_t count)
319*75f3e8e4SGabriel Somlo {
320*75f3e8e4SGabriel Somlo 	struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
321*75f3e8e4SGabriel Somlo 
322*75f3e8e4SGabriel Somlo 	if (pos > entry->f.size)
323*75f3e8e4SGabriel Somlo 		return -EINVAL;
324*75f3e8e4SGabriel Somlo 
325*75f3e8e4SGabriel Somlo 	if (count > entry->f.size - pos)
326*75f3e8e4SGabriel Somlo 		count = entry->f.size - pos;
327*75f3e8e4SGabriel Somlo 
328*75f3e8e4SGabriel Somlo 	fw_cfg_read_blob(entry->f.select, buf, pos, count);
329*75f3e8e4SGabriel Somlo 	return count;
330*75f3e8e4SGabriel Somlo }
331*75f3e8e4SGabriel Somlo 
332*75f3e8e4SGabriel Somlo static struct bin_attribute fw_cfg_sysfs_attr_raw = {
333*75f3e8e4SGabriel Somlo 	.attr = { .name = "raw", .mode = S_IRUSR },
334*75f3e8e4SGabriel Somlo 	.read = fw_cfg_sysfs_read_raw,
335*75f3e8e4SGabriel Somlo };
336*75f3e8e4SGabriel Somlo 
337*75f3e8e4SGabriel Somlo /* kobjects representing top-level and by_key folders */
338*75f3e8e4SGabriel Somlo static struct kobject *fw_cfg_top_ko;
339*75f3e8e4SGabriel Somlo static struct kobject *fw_cfg_sel_ko;
340*75f3e8e4SGabriel Somlo 
341*75f3e8e4SGabriel Somlo /* register an individual fw_cfg file */
342*75f3e8e4SGabriel Somlo static int fw_cfg_register_file(const struct fw_cfg_file *f)
343*75f3e8e4SGabriel Somlo {
344*75f3e8e4SGabriel Somlo 	int err;
345*75f3e8e4SGabriel Somlo 	struct fw_cfg_sysfs_entry *entry;
346*75f3e8e4SGabriel Somlo 
347*75f3e8e4SGabriel Somlo 	/* allocate new entry */
348*75f3e8e4SGabriel Somlo 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
349*75f3e8e4SGabriel Somlo 	if (!entry)
350*75f3e8e4SGabriel Somlo 		return -ENOMEM;
351*75f3e8e4SGabriel Somlo 
352*75f3e8e4SGabriel Somlo 	/* set file entry information */
353*75f3e8e4SGabriel Somlo 	memcpy(&entry->f, f, sizeof(struct fw_cfg_file));
354*75f3e8e4SGabriel Somlo 
355*75f3e8e4SGabriel Somlo 	/* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */
356*75f3e8e4SGabriel Somlo 	err = kobject_init_and_add(&entry->kobj, &fw_cfg_sysfs_entry_ktype,
357*75f3e8e4SGabriel Somlo 				   fw_cfg_sel_ko, "%d", entry->f.select);
358*75f3e8e4SGabriel Somlo 	if (err)
359*75f3e8e4SGabriel Somlo 		goto err_register;
360*75f3e8e4SGabriel Somlo 
361*75f3e8e4SGabriel Somlo 	/* add raw binary content access */
362*75f3e8e4SGabriel Somlo 	err = sysfs_create_bin_file(&entry->kobj, &fw_cfg_sysfs_attr_raw);
363*75f3e8e4SGabriel Somlo 	if (err)
364*75f3e8e4SGabriel Somlo 		goto err_add_raw;
365*75f3e8e4SGabriel Somlo 
366*75f3e8e4SGabriel Somlo 	/* success, add entry to global cache */
367*75f3e8e4SGabriel Somlo 	fw_cfg_sysfs_cache_enlist(entry);
368*75f3e8e4SGabriel Somlo 	return 0;
369*75f3e8e4SGabriel Somlo 
370*75f3e8e4SGabriel Somlo err_add_raw:
371*75f3e8e4SGabriel Somlo 	kobject_del(&entry->kobj);
372*75f3e8e4SGabriel Somlo err_register:
373*75f3e8e4SGabriel Somlo 	kfree(entry);
374*75f3e8e4SGabriel Somlo 	return err;
375*75f3e8e4SGabriel Somlo }
376*75f3e8e4SGabriel Somlo 
377*75f3e8e4SGabriel Somlo /* iterate over all fw_cfg directory entries, registering each one */
378*75f3e8e4SGabriel Somlo static int fw_cfg_register_dir_entries(void)
379*75f3e8e4SGabriel Somlo {
380*75f3e8e4SGabriel Somlo 	int ret = 0;
381*75f3e8e4SGabriel Somlo 	u32 count, i;
382*75f3e8e4SGabriel Somlo 	struct fw_cfg_file *dir;
383*75f3e8e4SGabriel Somlo 	size_t dir_size;
384*75f3e8e4SGabriel Somlo 
385*75f3e8e4SGabriel Somlo 	fw_cfg_read_blob(FW_CFG_FILE_DIR, &count, 0, sizeof(count));
386*75f3e8e4SGabriel Somlo 	count = be32_to_cpu(count);
387*75f3e8e4SGabriel Somlo 	dir_size = count * sizeof(struct fw_cfg_file);
388*75f3e8e4SGabriel Somlo 
389*75f3e8e4SGabriel Somlo 	dir = kmalloc(dir_size, GFP_KERNEL);
390*75f3e8e4SGabriel Somlo 	if (!dir)
391*75f3e8e4SGabriel Somlo 		return -ENOMEM;
392*75f3e8e4SGabriel Somlo 
393*75f3e8e4SGabriel Somlo 	fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size);
394*75f3e8e4SGabriel Somlo 
395*75f3e8e4SGabriel Somlo 	for (i = 0; i < count; i++) {
396*75f3e8e4SGabriel Somlo 		dir[i].size = be32_to_cpu(dir[i].size);
397*75f3e8e4SGabriel Somlo 		dir[i].select = be16_to_cpu(dir[i].select);
398*75f3e8e4SGabriel Somlo 		ret = fw_cfg_register_file(&dir[i]);
399*75f3e8e4SGabriel Somlo 		if (ret)
400*75f3e8e4SGabriel Somlo 			break;
401*75f3e8e4SGabriel Somlo 	}
402*75f3e8e4SGabriel Somlo 
403*75f3e8e4SGabriel Somlo 	kfree(dir);
404*75f3e8e4SGabriel Somlo 	return ret;
405*75f3e8e4SGabriel Somlo }
406*75f3e8e4SGabriel Somlo 
407*75f3e8e4SGabriel Somlo /* unregister top-level or by_key folder */
408*75f3e8e4SGabriel Somlo static inline void fw_cfg_kobj_cleanup(struct kobject *kobj)
409*75f3e8e4SGabriel Somlo {
410*75f3e8e4SGabriel Somlo 	kobject_del(kobj);
411*75f3e8e4SGabriel Somlo 	kobject_put(kobj);
412*75f3e8e4SGabriel Somlo }
413*75f3e8e4SGabriel Somlo 
414*75f3e8e4SGabriel Somlo static int fw_cfg_sysfs_probe(struct platform_device *pdev)
415*75f3e8e4SGabriel Somlo {
416*75f3e8e4SGabriel Somlo 	int err;
417*75f3e8e4SGabriel Somlo 
418*75f3e8e4SGabriel Somlo 	/* NOTE: If we supported multiple fw_cfg devices, we'd first create
419*75f3e8e4SGabriel Somlo 	 * a subdirectory named after e.g. pdev->id, then hang per-device
420*75f3e8e4SGabriel Somlo 	 * by_key subdirectories underneath it. However, only
421*75f3e8e4SGabriel Somlo 	 * one fw_cfg device exist system-wide, so if one was already found
422*75f3e8e4SGabriel Somlo 	 * earlier, we might as well stop here.
423*75f3e8e4SGabriel Somlo 	 */
424*75f3e8e4SGabriel Somlo 	if (fw_cfg_sel_ko)
425*75f3e8e4SGabriel Somlo 		return -EBUSY;
426*75f3e8e4SGabriel Somlo 
427*75f3e8e4SGabriel Somlo 	/* create by_key subdirectory of /sys/firmware/qemu_fw_cfg/ */
428*75f3e8e4SGabriel Somlo 	err = -ENOMEM;
429*75f3e8e4SGabriel Somlo 	fw_cfg_sel_ko = kobject_create_and_add("by_key", fw_cfg_top_ko);
430*75f3e8e4SGabriel Somlo 	if (!fw_cfg_sel_ko)
431*75f3e8e4SGabriel Somlo 		goto err_sel;
432*75f3e8e4SGabriel Somlo 
433*75f3e8e4SGabriel Somlo 	/* initialize fw_cfg device i/o from platform data */
434*75f3e8e4SGabriel Somlo 	err = fw_cfg_do_platform_probe(pdev);
435*75f3e8e4SGabriel Somlo 	if (err)
436*75f3e8e4SGabriel Somlo 		goto err_probe;
437*75f3e8e4SGabriel Somlo 
438*75f3e8e4SGabriel Somlo 	/* get revision number, add matching top-level attribute */
439*75f3e8e4SGabriel Somlo 	fw_cfg_read_blob(FW_CFG_ID, &fw_cfg_rev, 0, sizeof(fw_cfg_rev));
440*75f3e8e4SGabriel Somlo 	fw_cfg_rev = le32_to_cpu(fw_cfg_rev);
441*75f3e8e4SGabriel Somlo 	err = sysfs_create_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr);
442*75f3e8e4SGabriel Somlo 	if (err)
443*75f3e8e4SGabriel Somlo 		goto err_rev;
444*75f3e8e4SGabriel Somlo 
445*75f3e8e4SGabriel Somlo 	/* process fw_cfg file directory entry, registering each file */
446*75f3e8e4SGabriel Somlo 	err = fw_cfg_register_dir_entries();
447*75f3e8e4SGabriel Somlo 	if (err)
448*75f3e8e4SGabriel Somlo 		goto err_dir;
449*75f3e8e4SGabriel Somlo 
450*75f3e8e4SGabriel Somlo 	/* success */
451*75f3e8e4SGabriel Somlo 	pr_debug("fw_cfg: loaded.\n");
452*75f3e8e4SGabriel Somlo 	return 0;
453*75f3e8e4SGabriel Somlo 
454*75f3e8e4SGabriel Somlo err_dir:
455*75f3e8e4SGabriel Somlo 	fw_cfg_sysfs_cache_cleanup();
456*75f3e8e4SGabriel Somlo 	sysfs_remove_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr);
457*75f3e8e4SGabriel Somlo err_rev:
458*75f3e8e4SGabriel Somlo 	fw_cfg_io_cleanup();
459*75f3e8e4SGabriel Somlo err_probe:
460*75f3e8e4SGabriel Somlo 	fw_cfg_kobj_cleanup(fw_cfg_sel_ko);
461*75f3e8e4SGabriel Somlo err_sel:
462*75f3e8e4SGabriel Somlo 	return err;
463*75f3e8e4SGabriel Somlo }
464*75f3e8e4SGabriel Somlo 
465*75f3e8e4SGabriel Somlo static int fw_cfg_sysfs_remove(struct platform_device *pdev)
466*75f3e8e4SGabriel Somlo {
467*75f3e8e4SGabriel Somlo 	pr_debug("fw_cfg: unloading.\n");
468*75f3e8e4SGabriel Somlo 	fw_cfg_sysfs_cache_cleanup();
469*75f3e8e4SGabriel Somlo 	fw_cfg_kobj_cleanup(fw_cfg_sel_ko);
470*75f3e8e4SGabriel Somlo 	fw_cfg_io_cleanup();
471*75f3e8e4SGabriel Somlo 	return 0;
472*75f3e8e4SGabriel Somlo }
473*75f3e8e4SGabriel Somlo 
474*75f3e8e4SGabriel Somlo static const struct of_device_id fw_cfg_sysfs_mmio_match[] = {
475*75f3e8e4SGabriel Somlo 	{ .compatible = "qemu,fw-cfg-mmio", },
476*75f3e8e4SGabriel Somlo 	{},
477*75f3e8e4SGabriel Somlo };
478*75f3e8e4SGabriel Somlo MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match);
479*75f3e8e4SGabriel Somlo 
480*75f3e8e4SGabriel Somlo #ifdef CONFIG_ACPI
481*75f3e8e4SGabriel Somlo static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = {
482*75f3e8e4SGabriel Somlo 	{ "QEMU0002", },
483*75f3e8e4SGabriel Somlo 	{},
484*75f3e8e4SGabriel Somlo };
485*75f3e8e4SGabriel Somlo MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match);
486*75f3e8e4SGabriel Somlo #endif
487*75f3e8e4SGabriel Somlo 
488*75f3e8e4SGabriel Somlo static struct platform_driver fw_cfg_sysfs_driver = {
489*75f3e8e4SGabriel Somlo 	.probe = fw_cfg_sysfs_probe,
490*75f3e8e4SGabriel Somlo 	.remove = fw_cfg_sysfs_remove,
491*75f3e8e4SGabriel Somlo 	.driver = {
492*75f3e8e4SGabriel Somlo 		.name = "fw_cfg",
493*75f3e8e4SGabriel Somlo 		.of_match_table = fw_cfg_sysfs_mmio_match,
494*75f3e8e4SGabriel Somlo 		.acpi_match_table = ACPI_PTR(fw_cfg_sysfs_acpi_match),
495*75f3e8e4SGabriel Somlo 	},
496*75f3e8e4SGabriel Somlo };
497*75f3e8e4SGabriel Somlo 
498*75f3e8e4SGabriel Somlo #ifdef CONFIG_FW_CFG_SYSFS_CMDLINE
499*75f3e8e4SGabriel Somlo 
500*75f3e8e4SGabriel Somlo static struct platform_device *fw_cfg_cmdline_dev;
501*75f3e8e4SGabriel Somlo 
502*75f3e8e4SGabriel Somlo /* this probably belongs in e.g. include/linux/types.h,
503*75f3e8e4SGabriel Somlo  * but right now we are the only ones doing it...
504*75f3e8e4SGabriel Somlo  */
505*75f3e8e4SGabriel Somlo #ifdef CONFIG_PHYS_ADDR_T_64BIT
506*75f3e8e4SGabriel Somlo #define __PHYS_ADDR_PREFIX "ll"
507*75f3e8e4SGabriel Somlo #else
508*75f3e8e4SGabriel Somlo #define __PHYS_ADDR_PREFIX ""
509*75f3e8e4SGabriel Somlo #endif
510*75f3e8e4SGabriel Somlo 
511*75f3e8e4SGabriel Somlo /* use special scanf/printf modifier for phys_addr_t, resource_size_t */
512*75f3e8e4SGabriel Somlo #define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \
513*75f3e8e4SGabriel Somlo 			 ":%" __PHYS_ADDR_PREFIX "i" \
514*75f3e8e4SGabriel Somlo 			 ":%" __PHYS_ADDR_PREFIX "i%n"
515*75f3e8e4SGabriel Somlo 
516*75f3e8e4SGabriel Somlo #define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \
517*75f3e8e4SGabriel Somlo 			 "0x%" __PHYS_ADDR_PREFIX "x"
518*75f3e8e4SGabriel Somlo 
519*75f3e8e4SGabriel Somlo #define PH_ADDR_PR_3_FMT PH_ADDR_PR_1_FMT \
520*75f3e8e4SGabriel Somlo 			 ":%" __PHYS_ADDR_PREFIX "u" \
521*75f3e8e4SGabriel Somlo 			 ":%" __PHYS_ADDR_PREFIX "u"
522*75f3e8e4SGabriel Somlo 
523*75f3e8e4SGabriel Somlo static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
524*75f3e8e4SGabriel Somlo {
525*75f3e8e4SGabriel Somlo 	struct resource res[3] = {};
526*75f3e8e4SGabriel Somlo 	char *str;
527*75f3e8e4SGabriel Somlo 	phys_addr_t base;
528*75f3e8e4SGabriel Somlo 	resource_size_t size, ctrl_off, data_off;
529*75f3e8e4SGabriel Somlo 	int processed, consumed = 0;
530*75f3e8e4SGabriel Somlo 
531*75f3e8e4SGabriel Somlo 	/* only one fw_cfg device can exist system-wide, so if one
532*75f3e8e4SGabriel Somlo 	 * was processed on the command line already, we might as
533*75f3e8e4SGabriel Somlo 	 * well stop here.
534*75f3e8e4SGabriel Somlo 	 */
535*75f3e8e4SGabriel Somlo 	if (fw_cfg_cmdline_dev) {
536*75f3e8e4SGabriel Somlo 		/* avoid leaking previously registered device */
537*75f3e8e4SGabriel Somlo 		platform_device_unregister(fw_cfg_cmdline_dev);
538*75f3e8e4SGabriel Somlo 		return -EINVAL;
539*75f3e8e4SGabriel Somlo 	}
540*75f3e8e4SGabriel Somlo 
541*75f3e8e4SGabriel Somlo 	/* consume "<size>" portion of command line argument */
542*75f3e8e4SGabriel Somlo 	size = memparse(arg, &str);
543*75f3e8e4SGabriel Somlo 
544*75f3e8e4SGabriel Somlo 	/* get "@<base>[:<ctrl_off>:<data_off>]" chunks */
545*75f3e8e4SGabriel Somlo 	processed = sscanf(str, PH_ADDR_SCAN_FMT,
546*75f3e8e4SGabriel Somlo 			   &base, &consumed,
547*75f3e8e4SGabriel Somlo 			   &ctrl_off, &data_off, &consumed);
548*75f3e8e4SGabriel Somlo 
549*75f3e8e4SGabriel Somlo 	/* sscanf() must process precisely 1 or 3 chunks:
550*75f3e8e4SGabriel Somlo 	 * <base> is mandatory, optionally followed by <ctrl_off>
551*75f3e8e4SGabriel Somlo 	 * and <data_off>;
552*75f3e8e4SGabriel Somlo 	 * there must be no extra characters after the last chunk,
553*75f3e8e4SGabriel Somlo 	 * so str[consumed] must be '\0'.
554*75f3e8e4SGabriel Somlo 	 */
555*75f3e8e4SGabriel Somlo 	if (str[consumed] ||
556*75f3e8e4SGabriel Somlo 	    (processed != 1 && processed != 3))
557*75f3e8e4SGabriel Somlo 		return -EINVAL;
558*75f3e8e4SGabriel Somlo 
559*75f3e8e4SGabriel Somlo 	res[0].start = base;
560*75f3e8e4SGabriel Somlo 	res[0].end = base + size - 1;
561*75f3e8e4SGabriel Somlo 	res[0].flags = !strcmp(kp->name, "mmio") ? IORESOURCE_MEM :
562*75f3e8e4SGabriel Somlo 						   IORESOURCE_IO;
563*75f3e8e4SGabriel Somlo 
564*75f3e8e4SGabriel Somlo 	/* insert register offsets, if provided */
565*75f3e8e4SGabriel Somlo 	if (processed > 1) {
566*75f3e8e4SGabriel Somlo 		res[1].name = "ctrl";
567*75f3e8e4SGabriel Somlo 		res[1].start = ctrl_off;
568*75f3e8e4SGabriel Somlo 		res[1].flags = IORESOURCE_REG;
569*75f3e8e4SGabriel Somlo 		res[2].name = "data";
570*75f3e8e4SGabriel Somlo 		res[2].start = data_off;
571*75f3e8e4SGabriel Somlo 		res[2].flags = IORESOURCE_REG;
572*75f3e8e4SGabriel Somlo 	}
573*75f3e8e4SGabriel Somlo 
574*75f3e8e4SGabriel Somlo 	/* "processed" happens to nicely match the number of resources
575*75f3e8e4SGabriel Somlo 	 * we need to pass in to this platform device.
576*75f3e8e4SGabriel Somlo 	 */
577*75f3e8e4SGabriel Somlo 	fw_cfg_cmdline_dev = platform_device_register_simple("fw_cfg",
578*75f3e8e4SGabriel Somlo 					PLATFORM_DEVID_NONE, res, processed);
579*75f3e8e4SGabriel Somlo 	if (IS_ERR(fw_cfg_cmdline_dev))
580*75f3e8e4SGabriel Somlo 		return PTR_ERR(fw_cfg_cmdline_dev);
581*75f3e8e4SGabriel Somlo 
582*75f3e8e4SGabriel Somlo 	return 0;
583*75f3e8e4SGabriel Somlo }
584*75f3e8e4SGabriel Somlo 
585*75f3e8e4SGabriel Somlo static int fw_cfg_cmdline_get(char *buf, const struct kernel_param *kp)
586*75f3e8e4SGabriel Somlo {
587*75f3e8e4SGabriel Somlo 	/* stay silent if device was not configured via the command
588*75f3e8e4SGabriel Somlo 	 * line, or if the parameter name (ioport/mmio) doesn't match
589*75f3e8e4SGabriel Somlo 	 * the device setting
590*75f3e8e4SGabriel Somlo 	 */
591*75f3e8e4SGabriel Somlo 	if (!fw_cfg_cmdline_dev ||
592*75f3e8e4SGabriel Somlo 	    (!strcmp(kp->name, "mmio") ^
593*75f3e8e4SGabriel Somlo 	     (fw_cfg_cmdline_dev->resource[0].flags == IORESOURCE_MEM)))
594*75f3e8e4SGabriel Somlo 		return 0;
595*75f3e8e4SGabriel Somlo 
596*75f3e8e4SGabriel Somlo 	switch (fw_cfg_cmdline_dev->num_resources) {
597*75f3e8e4SGabriel Somlo 	case 1:
598*75f3e8e4SGabriel Somlo 		return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_1_FMT,
599*75f3e8e4SGabriel Somlo 				resource_size(&fw_cfg_cmdline_dev->resource[0]),
600*75f3e8e4SGabriel Somlo 				fw_cfg_cmdline_dev->resource[0].start);
601*75f3e8e4SGabriel Somlo 	case 3:
602*75f3e8e4SGabriel Somlo 		return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_3_FMT,
603*75f3e8e4SGabriel Somlo 				resource_size(&fw_cfg_cmdline_dev->resource[0]),
604*75f3e8e4SGabriel Somlo 				fw_cfg_cmdline_dev->resource[0].start,
605*75f3e8e4SGabriel Somlo 				fw_cfg_cmdline_dev->resource[1].start,
606*75f3e8e4SGabriel Somlo 				fw_cfg_cmdline_dev->resource[2].start);
607*75f3e8e4SGabriel Somlo 	}
608*75f3e8e4SGabriel Somlo 
609*75f3e8e4SGabriel Somlo 	/* Should never get here */
610*75f3e8e4SGabriel Somlo 	WARN(1, "Unexpected number of resources: %d\n",
611*75f3e8e4SGabriel Somlo 		fw_cfg_cmdline_dev->num_resources);
612*75f3e8e4SGabriel Somlo 	return 0;
613*75f3e8e4SGabriel Somlo }
614*75f3e8e4SGabriel Somlo 
615*75f3e8e4SGabriel Somlo static const struct kernel_param_ops fw_cfg_cmdline_param_ops = {
616*75f3e8e4SGabriel Somlo 	.set = fw_cfg_cmdline_set,
617*75f3e8e4SGabriel Somlo 	.get = fw_cfg_cmdline_get,
618*75f3e8e4SGabriel Somlo };
619*75f3e8e4SGabriel Somlo 
620*75f3e8e4SGabriel Somlo device_param_cb(ioport, &fw_cfg_cmdline_param_ops, NULL, S_IRUSR);
621*75f3e8e4SGabriel Somlo device_param_cb(mmio, &fw_cfg_cmdline_param_ops, NULL, S_IRUSR);
622*75f3e8e4SGabriel Somlo 
623*75f3e8e4SGabriel Somlo #endif /* CONFIG_FW_CFG_SYSFS_CMDLINE */
624*75f3e8e4SGabriel Somlo 
625*75f3e8e4SGabriel Somlo static int __init fw_cfg_sysfs_init(void)
626*75f3e8e4SGabriel Somlo {
627*75f3e8e4SGabriel Somlo 	/* create /sys/firmware/qemu_fw_cfg/ top level directory */
628*75f3e8e4SGabriel Somlo 	fw_cfg_top_ko = kobject_create_and_add("qemu_fw_cfg", firmware_kobj);
629*75f3e8e4SGabriel Somlo 	if (!fw_cfg_top_ko)
630*75f3e8e4SGabriel Somlo 		return -ENOMEM;
631*75f3e8e4SGabriel Somlo 
632*75f3e8e4SGabriel Somlo 	return platform_driver_register(&fw_cfg_sysfs_driver);
633*75f3e8e4SGabriel Somlo }
634*75f3e8e4SGabriel Somlo 
635*75f3e8e4SGabriel Somlo static void __exit fw_cfg_sysfs_exit(void)
636*75f3e8e4SGabriel Somlo {
637*75f3e8e4SGabriel Somlo 	platform_driver_unregister(&fw_cfg_sysfs_driver);
638*75f3e8e4SGabriel Somlo 
639*75f3e8e4SGabriel Somlo #ifdef CONFIG_FW_CFG_SYSFS_CMDLINE
640*75f3e8e4SGabriel Somlo 	platform_device_unregister(fw_cfg_cmdline_dev);
641*75f3e8e4SGabriel Somlo #endif
642*75f3e8e4SGabriel Somlo 
643*75f3e8e4SGabriel Somlo 	/* clean up /sys/firmware/qemu_fw_cfg/ */
644*75f3e8e4SGabriel Somlo 	fw_cfg_kobj_cleanup(fw_cfg_top_ko);
645*75f3e8e4SGabriel Somlo }
646*75f3e8e4SGabriel Somlo 
647*75f3e8e4SGabriel Somlo module_init(fw_cfg_sysfs_init);
648*75f3e8e4SGabriel Somlo module_exit(fw_cfg_sysfs_exit);
649