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 = 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, id: 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, res: r, num: 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(dev: &pdev->dev, properties: 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 | |
120 | err: |
121 | platform_device_put(pdev); |
122 | return NULL; |
123 | } |
124 | EXPORT_SYMBOL(ipmi_platform_add); |
125 | |