Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
4dea2d1a SB |
2 | /* |
3 | * Copyright 2016-17 IBM Corp. | |
4dea2d1a SB |
4 | */ |
5 | ||
6 | #define pr_fmt(fmt) "vas: " fmt | |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/export.h> | |
11 | #include <linux/types.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/of_platform.h> | |
15 | #include <linux/of_address.h> | |
16 | #include <linux/of.h> | |
ca03258b | 17 | #include <asm/prom.h> |
4dea2d1a SB |
18 | |
19 | #include "vas.h" | |
20 | ||
ece4e512 | 21 | DEFINE_MUTEX(vas_mutex); |
4dea2d1a SB |
22 | static LIST_HEAD(vas_instances); |
23 | ||
ca03258b SB |
24 | static DEFINE_PER_CPU(int, cpu_vas_id); |
25 | ||
4dea2d1a SB |
26 | static int init_vas_instance(struct platform_device *pdev) |
27 | { | |
ca03258b | 28 | int rc, cpu, vasid; |
4dea2d1a SB |
29 | struct resource *res; |
30 | struct vas_instance *vinst; | |
31 | struct device_node *dn = pdev->dev.of_node; | |
32 | ||
33 | rc = of_property_read_u32(dn, "ibm,vas-id", &vasid); | |
34 | if (rc) { | |
35 | pr_err("No ibm,vas-id property for %s?\n", pdev->name); | |
36 | return -ENODEV; | |
37 | } | |
38 | ||
39 | if (pdev->num_resources != 4) { | |
40 | pr_err("Unexpected DT configuration for [%s, %d]\n", | |
41 | pdev->name, vasid); | |
42 | return -ENODEV; | |
43 | } | |
44 | ||
45 | vinst = kzalloc(sizeof(*vinst), GFP_KERNEL); | |
46 | if (!vinst) | |
47 | return -ENOMEM; | |
48 | ||
49 | INIT_LIST_HEAD(&vinst->node); | |
50 | ida_init(&vinst->ida); | |
51 | mutex_init(&vinst->mutex); | |
52 | vinst->vas_id = vasid; | |
53 | vinst->pdev = pdev; | |
54 | ||
55 | res = &pdev->resource[0]; | |
56 | vinst->hvwc_bar_start = res->start; | |
57 | ||
58 | res = &pdev->resource[1]; | |
59 | vinst->uwc_bar_start = res->start; | |
60 | ||
61 | res = &pdev->resource[2]; | |
62 | vinst->paste_base_addr = res->start; | |
63 | ||
64 | res = &pdev->resource[3]; | |
65 | if (res->end > 62) { | |
66 | pr_err("Bad 'paste_win_id_shift' in DT, %llx\n", res->end); | |
67 | goto free_vinst; | |
68 | } | |
69 | ||
70 | vinst->paste_win_id_shift = 63 - res->end; | |
71 | ||
72 | pr_devel("Initialized instance [%s, %d], paste_base 0x%llx, " | |
73 | "paste_win_id_shift 0x%llx\n", pdev->name, vasid, | |
74 | vinst->paste_base_addr, vinst->paste_win_id_shift); | |
75 | ||
ca03258b SB |
76 | for_each_possible_cpu(cpu) { |
77 | if (cpu_to_chip_id(cpu) == of_get_ibm_chip_id(dn)) | |
78 | per_cpu(cpu_vas_id, cpu) = vasid; | |
79 | } | |
80 | ||
4dea2d1a SB |
81 | mutex_lock(&vas_mutex); |
82 | list_add(&vinst->node, &vas_instances); | |
83 | mutex_unlock(&vas_mutex); | |
84 | ||
ece4e512 SB |
85 | vas_instance_init_dbgdir(vinst); |
86 | ||
4dea2d1a SB |
87 | dev_set_drvdata(&pdev->dev, vinst); |
88 | ||
89 | return 0; | |
90 | ||
91 | free_vinst: | |
92 | kfree(vinst); | |
93 | return -ENODEV; | |
94 | ||
95 | } | |
96 | ||
97 | /* | |
98 | * Although this is read/used multiple times, it is written to only | |
99 | * during initialization. | |
100 | */ | |
101 | struct vas_instance *find_vas_instance(int vasid) | |
102 | { | |
103 | struct list_head *ent; | |
104 | struct vas_instance *vinst; | |
105 | ||
106 | mutex_lock(&vas_mutex); | |
ca03258b SB |
107 | |
108 | if (vasid == -1) | |
109 | vasid = per_cpu(cpu_vas_id, smp_processor_id()); | |
110 | ||
4dea2d1a SB |
111 | list_for_each(ent, &vas_instances) { |
112 | vinst = list_entry(ent, struct vas_instance, node); | |
113 | if (vinst->vas_id == vasid) { | |
114 | mutex_unlock(&vas_mutex); | |
115 | return vinst; | |
116 | } | |
117 | } | |
118 | mutex_unlock(&vas_mutex); | |
119 | ||
120 | pr_devel("Instance %d not found\n", vasid); | |
121 | return NULL; | |
122 | } | |
123 | ||
d4ef61b5 SB |
124 | int chip_to_vas_id(int chipid) |
125 | { | |
126 | int cpu; | |
127 | ||
128 | for_each_possible_cpu(cpu) { | |
129 | if (cpu_to_chip_id(cpu) == chipid) | |
130 | return per_cpu(cpu_vas_id, cpu); | |
131 | } | |
132 | return -1; | |
133 | } | |
62b49c42 | 134 | EXPORT_SYMBOL(chip_to_vas_id); |
d4ef61b5 | 135 | |
4dea2d1a SB |
136 | static int vas_probe(struct platform_device *pdev) |
137 | { | |
138 | return init_vas_instance(pdev); | |
139 | } | |
140 | ||
141 | static const struct of_device_id powernv_vas_match[] = { | |
142 | { .compatible = "ibm,vas",}, | |
143 | {}, | |
144 | }; | |
145 | ||
146 | static struct platform_driver vas_driver = { | |
147 | .driver = { | |
148 | .name = "vas", | |
149 | .of_match_table = powernv_vas_match, | |
150 | }, | |
151 | .probe = vas_probe, | |
152 | }; | |
153 | ||
154 | static int __init vas_init(void) | |
155 | { | |
156 | int found = 0; | |
157 | struct device_node *dn; | |
158 | ||
159 | platform_driver_register(&vas_driver); | |
160 | ||
161 | for_each_compatible_node(dn, NULL, "ibm,vas") { | |
162 | of_platform_device_create(dn, NULL, NULL); | |
163 | found++; | |
164 | } | |
165 | ||
45ddea8a SB |
166 | if (!found) { |
167 | platform_driver_unregister(&vas_driver); | |
4dea2d1a | 168 | return -ENODEV; |
45ddea8a | 169 | } |
4dea2d1a SB |
170 | |
171 | pr_devel("Found %d instances\n", found); | |
172 | ||
173 | return 0; | |
174 | } | |
175 | device_initcall(vas_init); |