summaryrefslogtreecommitdiffstats
path: root/drivers/char/ipmi/ipmi_plat_data.c
blob: 28471ff2a3a3e3400c81dbc6f59862e8815bb1dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// SPDX-License-Identifier: GPL-2.0+

/*
 * Add an IPMI platform device.
 */

#include <linux/platform_device.h>
#include "ipmi_plat_data.h"
#include "ipmi_si.h"

struct platform_device *ipmi_platform_add(const char *name, unsigned int inst,
					  struct ipmi_plat_data *p)
{
	struct platform_device *pdev;
	unsigned int num_r = 1, size = 0, pidx = 0;
	struct resource r[4];
	struct property_entry pr[6];
	u32 flags;
	int rv;

	memset(pr, 0, sizeof(pr));
	memset(r, 0, sizeof(r));

	if (p->iftype == IPMI_PLAT_IF_SI) {
		if (p->type == SI_BT)
			size = 3;
		else if (p->type != SI_TYPE_INVALID)
			size = 2;

		if (p->regsize == 0)
			p->regsize = DEFAULT_REGSIZE;
		if (p->regspacing == 0)
			p->regspacing = p->regsize;

		pr[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", p->type);
	} else if (p->iftype == IPMI_PLAT_IF_SSIF) {
		pr[pidx++] = PROPERTY_ENTRY_U16("i2c-addr", p->addr);
	}

	if (p->slave_addr)
		pr[pidx++] = PROPERTY_ENTRY_U8("slave-addr", p->slave_addr);
	pr[pidx++] = PROPERTY_ENTRY_U8("addr-source", p->addr_source);
	if (p->regshift)
		pr[pidx++] = PROPERTY_ENTRY_U8("reg-shift", p->regshift);
	pr[pidx++] = PROPERTY_ENTRY_U8("reg-size", p->regsize);
	/* Last entry must be left NULL to terminate it. */

	pdev = platform_device_alloc(name, inst);
	if (!pdev) {
		pr_err("Error allocating IPMI platform device %s.%d\n",
		       name, inst);
		return NULL;
	}

	if (size == 0)
		/* An invalid or SSIF interface, no resources. */
		goto add_properties;

	/*
	 * Register spacing is derived from the resources in
	 * the IPMI platform code.
	 */

	if (p->space == IPMI_IO_ADDR_SPACE)
		flags = IORESOURCE_IO;
	else
		flags = IORESOURCE_MEM;

	r[0].start = p->addr;
	r[0].end = r[0].start + p->regsize - 1;
	r[0].name = "IPMI Address 1";
	r[0].flags = flags;

	if (size > 1) {
		r[1].start = r[0].start + p->regspacing;
		r[1].end = r[1].start + p->regsize - 1;
		r[1].name = "IPMI Address 2";
		r[1].flags = flags;
		num_r++;
	}

	if (size > 2) {
		r[2].start = r[1].start + p->regspacing;
		r[2].end = r[2].start + p->regsize - 1;
		r[2].name = "IPMI Address 3";
		r[2].flags = flags;
		num_r++;
	}

	if (p->irq) {
		r[num_r].start = p->irq;
		r[num_r].end = p->irq;
		r[num_r].name = "IPMI IRQ";
		r[num_r].flags = IORESOURCE_IRQ;
		num_r++;
	}

	rv = platform_device_add_resources(pdev, r, num_r);
	if (rv) {
		dev_err(&pdev->dev,
			"Unable to add hard-code resources: %d\n", rv);
		goto err;
	}
 add_properties:
	rv = platform_device_add_properties(pdev, pr);
	if (rv) {
		dev_err(&pdev->dev,
			"Unable to add hard-code properties: %d\n", rv);
		goto err;
	}

	rv = platform_device_add(pdev);
	if (rv) {
		dev_err(&pdev->dev,
			"Unable to add hard-code device: %d\n", rv);
		goto err;
	}
	return pdev;

err:
	platform_device_put(pdev);
	return NULL;
}
EXPORT_SYMBOL(ipmi_platform_add);