1 // SPDX-License-Identifier: GPL-2.0+
2 
3 /*
4  * Add an IPMI platform device.
5  */
6 
7 #include <linux/platform_device.h>
8 #include "ipmi_plat_data.h"
9 #include "ipmi_si.h"
10 
11 struct platform_device *ipmi_platform_add(const char *name, unsigned int inst,
12 					  struct ipmi_plat_data *p)
13 {
14 	struct platform_device *pdev;
15 	unsigned int num_r = 1, size, pidx = 0;
16 	struct resource r[4];
17 	struct property_entry pr[6];
18 	u32 flags;
19 	int rv;
20 
21 	memset(pr, 0, sizeof(pr));
22 	memset(r, 0, sizeof(r));
23 
24 	if (p->type == SI_BT)
25 		size = 3;
26 	else if (p->type == SI_TYPE_INVALID)
27 		size = 0;
28 	else
29 		size = 2;
30 
31 	if (p->regsize == 0)
32 		p->regsize = DEFAULT_REGSIZE;
33 	if (p->regspacing == 0)
34 		p->regspacing = p->regsize;
35 
36 	pr[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", p->type);
37 	if (p->slave_addr)
38 		pr[pidx++] = PROPERTY_ENTRY_U8("slave-addr", p->slave_addr);
39 	pr[pidx++] = PROPERTY_ENTRY_U8("addr-source", p->addr_source);
40 	if (p->regshift)
41 		pr[pidx++] = PROPERTY_ENTRY_U8("reg-shift", p->regshift);
42 	pr[pidx++] = PROPERTY_ENTRY_U8("reg-size", p->regsize);
43 	/* Last entry must be left NULL to terminate it. */
44 
45 	pdev = platform_device_alloc(name, inst);
46 	if (!pdev) {
47 		pr_err("Error allocating IPMI platform device %s.%d\n",
48 		       name, inst);
49 		return NULL;
50 	}
51 
52 	if (size == 0)
53 		/* An invalid or SSIF interface, no resources. */
54 		goto add_properties;
55 
56 	/*
57 	 * Register spacing is derived from the resources in
58 	 * the IPMI platform code.
59 	 */
60 
61 	if (p->space == IPMI_IO_ADDR_SPACE)
62 		flags = IORESOURCE_IO;
63 	else
64 		flags = IORESOURCE_MEM;
65 
66 	r[0].start = p->addr;
67 	r[0].end = r[0].start + p->regsize - 1;
68 	r[0].name = "IPMI Address 1";
69 	r[0].flags = flags;
70 
71 	if (size > 1) {
72 		r[1].start = r[0].start + p->regspacing;
73 		r[1].end = r[1].start + p->regsize - 1;
74 		r[1].name = "IPMI Address 2";
75 		r[1].flags = flags;
76 		num_r++;
77 	}
78 
79 	if (size > 2) {
80 		r[2].start = r[1].start + p->regspacing;
81 		r[2].end = r[2].start + p->regsize - 1;
82 		r[2].name = "IPMI Address 3";
83 		r[2].flags = flags;
84 		num_r++;
85 	}
86 
87 	if (p->irq) {
88 		r[num_r].start = p->irq;
89 		r[num_r].end = p->irq;
90 		r[num_r].name = "IPMI IRQ";
91 		r[num_r].flags = IORESOURCE_IRQ;
92 		num_r++;
93 	}
94 
95 	rv = platform_device_add_resources(pdev, r, num_r);
96 	if (rv) {
97 		dev_err(&pdev->dev,
98 			"Unable to add hard-code resources: %d\n", rv);
99 		goto err;
100 	}
101  add_properties:
102 	rv = platform_device_add_properties(pdev, pr);
103 	if (rv) {
104 		dev_err(&pdev->dev,
105 			"Unable to add hard-code properties: %d\n", rv);
106 		goto err;
107 	}
108 
109 	rv = platform_device_add(pdev);
110 	if (rv) {
111 		dev_err(&pdev->dev,
112 			"Unable to add hard-code device: %d\n", rv);
113 		goto err;
114 	}
115 	return pdev;
116 
117 err:
118 	platform_device_put(pdev);
119 	return NULL;
120 }
121 EXPORT_SYMBOL(ipmi_platform_add);
122