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
11struct 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 = 0, 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->iftype == IPMI_PLAT_IF_SI) {
25		if (p->type == SI_BT)
26			size = 3;
27		else if (p->type != SI_TYPE_INVALID)
28			size = 2;
29
30		if (p->regsize == 0)
31			p->regsize = DEFAULT_REGSIZE;
32		if (p->regspacing == 0)
33			p->regspacing = p->regsize;
34
35		pr[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", p->type);
36	} else if (p->iftype == IPMI_PLAT_IF_SSIF) {
37		pr[pidx++] = PROPERTY_ENTRY_U16("i2c-addr", p->addr);
38	}
39
40	if (p->slave_addr)
41		pr[pidx++] = PROPERTY_ENTRY_U8("slave-addr", p->slave_addr);
42	pr[pidx++] = PROPERTY_ENTRY_U8("addr-source", p->addr_source);
43	if (p->regshift)
44		pr[pidx++] = PROPERTY_ENTRY_U8("reg-shift", p->regshift);
45	pr[pidx++] = PROPERTY_ENTRY_U8("reg-size", p->regsize);
46	/* Last entry must be left NULL to terminate it. */
47
48	pdev = platform_device_alloc(name, inst);
49	if (!pdev) {
50		pr_err("Error allocating IPMI platform device %s.%d\n",
51		       name, inst);
52		return NULL;
53	}
54
55	if (size == 0)
56		/* An invalid or SSIF interface, no resources. */
57		goto add_properties;
58
59	/*
60	 * Register spacing is derived from the resources in
61	 * the IPMI platform code.
62	 */
63
64	if (p->space == IPMI_IO_ADDR_SPACE)
65		flags = IORESOURCE_IO;
66	else
67		flags = IORESOURCE_MEM;
68
69	r[0].start = p->addr;
70	r[0].end = r[0].start + p->regsize - 1;
71	r[0].name = "IPMI Address 1";
72	r[0].flags = flags;
73
74	if (size > 1) {
75		r[1].start = r[0].start + p->regspacing;
76		r[1].end = r[1].start + p->regsize - 1;
77		r[1].name = "IPMI Address 2";
78		r[1].flags = flags;
79		num_r++;
80	}
81
82	if (size > 2) {
83		r[2].start = r[1].start + p->regspacing;
84		r[2].end = r[2].start + p->regsize - 1;
85		r[2].name = "IPMI Address 3";
86		r[2].flags = flags;
87		num_r++;
88	}
89
90	if (p->irq) {
91		r[num_r].start = p->irq;
92		r[num_r].end = p->irq;
93		r[num_r].name = "IPMI IRQ";
94		r[num_r].flags = IORESOURCE_IRQ;
95		num_r++;
96	}
97
98	rv = platform_device_add_resources(pdev, r, num_r);
99	if (rv) {
100		dev_err(&pdev->dev,
101			"Unable to add hard-code resources: %d\n", rv);
102		goto err;
103	}
104 add_properties:
105	rv = device_create_managed_software_node(&pdev->dev, pr, NULL);
106	if (rv) {
107		dev_err(&pdev->dev,
108			"Unable to add hard-code properties: %d\n", rv);
109		goto err;
110	}
111
112	rv = platform_device_add(pdev);
113	if (rv) {
114		dev_err(&pdev->dev,
115			"Unable to add hard-code device: %d\n", rv);
116		goto err;
117	}
118	return pdev;
119
120err:
121	platform_device_put(pdev);
122	return NULL;
123}
124EXPORT_SYMBOL(ipmi_platform_add);
125