xref: /openbmc/linux/drivers/misc/cxl/flash.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2594ff7d0SChristophe Lombard #include <linux/kernel.h>
3594ff7d0SChristophe Lombard #include <linux/fs.h>
4594ff7d0SChristophe Lombard #include <linux/semaphore.h>
5594ff7d0SChristophe Lombard #include <linux/slab.h>
6594ff7d0SChristophe Lombard #include <linux/uaccess.h>
7*d8d2af70SChristophe Leroy #include <linux/of.h>
8594ff7d0SChristophe Lombard #include <asm/rtas.h>
9594ff7d0SChristophe Lombard 
10594ff7d0SChristophe Lombard #include "cxl.h"
11594ff7d0SChristophe Lombard #include "hcalls.h"
12594ff7d0SChristophe Lombard 
13594ff7d0SChristophe Lombard #define DOWNLOAD_IMAGE 1
14594ff7d0SChristophe Lombard #define VALIDATE_IMAGE 2
15594ff7d0SChristophe Lombard 
16594ff7d0SChristophe Lombard struct ai_header {
17594ff7d0SChristophe Lombard 	u16 version;
18594ff7d0SChristophe Lombard 	u8  reserved0[6];
19594ff7d0SChristophe Lombard 	u16 vendor;
20594ff7d0SChristophe Lombard 	u16 device;
21594ff7d0SChristophe Lombard 	u16 subsystem_vendor;
22594ff7d0SChristophe Lombard 	u16 subsystem;
23594ff7d0SChristophe Lombard 	u64 image_offset;
24594ff7d0SChristophe Lombard 	u64 image_length;
25594ff7d0SChristophe Lombard 	u8  reserved1[96];
26594ff7d0SChristophe Lombard };
27594ff7d0SChristophe Lombard 
28594ff7d0SChristophe Lombard static struct semaphore sem;
2964417a39SAndrew Donnellan static unsigned long *buffer[CXL_AI_MAX_ENTRIES];
3064417a39SAndrew Donnellan static struct sg_list *le;
31594ff7d0SChristophe Lombard static u64 continue_token;
32594ff7d0SChristophe Lombard static unsigned int transfer;
33594ff7d0SChristophe Lombard 
34594ff7d0SChristophe Lombard struct update_props_workarea {
35594ff7d0SChristophe Lombard 	__be32 phandle;
36594ff7d0SChristophe Lombard 	__be32 state;
37594ff7d0SChristophe Lombard 	__be64 reserved;
38594ff7d0SChristophe Lombard 	__be32 nprops;
39594ff7d0SChristophe Lombard } __packed;
40594ff7d0SChristophe Lombard 
41594ff7d0SChristophe Lombard struct update_nodes_workarea {
42594ff7d0SChristophe Lombard 	__be32 state;
43594ff7d0SChristophe Lombard 	__be64 unit_address;
44594ff7d0SChristophe Lombard 	__be32 reserved;
45594ff7d0SChristophe Lombard } __packed;
46594ff7d0SChristophe Lombard 
47594ff7d0SChristophe Lombard #define DEVICE_SCOPE 3
48594ff7d0SChristophe Lombard #define NODE_ACTION_MASK	0xff000000
49594ff7d0SChristophe Lombard #define NODE_COUNT_MASK		0x00ffffff
50594ff7d0SChristophe Lombard #define OPCODE_DELETE	0x01000000
51594ff7d0SChristophe Lombard #define OPCODE_UPDATE	0x02000000
52594ff7d0SChristophe Lombard #define OPCODE_ADD	0x03000000
53594ff7d0SChristophe Lombard 
rcall(int token,char * buf,s32 scope)54594ff7d0SChristophe Lombard static int rcall(int token, char *buf, s32 scope)
55594ff7d0SChristophe Lombard {
56594ff7d0SChristophe Lombard 	int rc;
57594ff7d0SChristophe Lombard 
58594ff7d0SChristophe Lombard 	spin_lock(&rtas_data_buf_lock);
59594ff7d0SChristophe Lombard 
60594ff7d0SChristophe Lombard 	memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
61594ff7d0SChristophe Lombard 	rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope);
62594ff7d0SChristophe Lombard 	memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
63594ff7d0SChristophe Lombard 
64594ff7d0SChristophe Lombard 	spin_unlock(&rtas_data_buf_lock);
65594ff7d0SChristophe Lombard 	return rc;
66594ff7d0SChristophe Lombard }
67594ff7d0SChristophe Lombard 
update_property(struct device_node * dn,const char * name,u32 vd,char * value)68594ff7d0SChristophe Lombard static int update_property(struct device_node *dn, const char *name,
69594ff7d0SChristophe Lombard 			   u32 vd, char *value)
70594ff7d0SChristophe Lombard {
71594ff7d0SChristophe Lombard 	struct property *new_prop;
72594ff7d0SChristophe Lombard 	u32 *val;
73594ff7d0SChristophe Lombard 	int rc;
74594ff7d0SChristophe Lombard 
75594ff7d0SChristophe Lombard 	new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
76594ff7d0SChristophe Lombard 	if (!new_prop)
77594ff7d0SChristophe Lombard 		return -ENOMEM;
78594ff7d0SChristophe Lombard 
79594ff7d0SChristophe Lombard 	new_prop->name = kstrdup(name, GFP_KERNEL);
80594ff7d0SChristophe Lombard 	if (!new_prop->name) {
81594ff7d0SChristophe Lombard 		kfree(new_prop);
82594ff7d0SChristophe Lombard 		return -ENOMEM;
83594ff7d0SChristophe Lombard 	}
84594ff7d0SChristophe Lombard 
85594ff7d0SChristophe Lombard 	new_prop->length = vd;
86594ff7d0SChristophe Lombard 	new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);
87594ff7d0SChristophe Lombard 	if (!new_prop->value) {
88594ff7d0SChristophe Lombard 		kfree(new_prop->name);
89594ff7d0SChristophe Lombard 		kfree(new_prop);
90594ff7d0SChristophe Lombard 		return -ENOMEM;
91594ff7d0SChristophe Lombard 	}
92594ff7d0SChristophe Lombard 	memcpy(new_prop->value, value, vd);
93594ff7d0SChristophe Lombard 
94594ff7d0SChristophe Lombard 	val = (u32 *)new_prop->value;
95594ff7d0SChristophe Lombard 	rc = cxl_update_properties(dn, new_prop);
96aa2eb860SRob Herring 	pr_devel("%pOFn: update property (%s, length: %i, value: %#x)\n",
97aa2eb860SRob Herring 		  dn, name, vd, be32_to_cpu(*val));
98594ff7d0SChristophe Lombard 
99594ff7d0SChristophe Lombard 	if (rc) {
100594ff7d0SChristophe Lombard 		kfree(new_prop->name);
101594ff7d0SChristophe Lombard 		kfree(new_prop->value);
102594ff7d0SChristophe Lombard 		kfree(new_prop);
103594ff7d0SChristophe Lombard 	}
104594ff7d0SChristophe Lombard 	return rc;
105594ff7d0SChristophe Lombard }
106594ff7d0SChristophe Lombard 
update_node(__be32 phandle,s32 scope)107594ff7d0SChristophe Lombard static int update_node(__be32 phandle, s32 scope)
108594ff7d0SChristophe Lombard {
109594ff7d0SChristophe Lombard 	struct update_props_workarea *upwa;
110594ff7d0SChristophe Lombard 	struct device_node *dn;
111594ff7d0SChristophe Lombard 	int i, rc, ret;
112594ff7d0SChristophe Lombard 	char *prop_data;
113594ff7d0SChristophe Lombard 	char *buf;
114594ff7d0SChristophe Lombard 	int token;
115594ff7d0SChristophe Lombard 	u32 nprops;
116594ff7d0SChristophe Lombard 	u32 vd;
117594ff7d0SChristophe Lombard 
118594ff7d0SChristophe Lombard 	token = rtas_token("ibm,update-properties");
119594ff7d0SChristophe Lombard 	if (token == RTAS_UNKNOWN_SERVICE)
120594ff7d0SChristophe Lombard 		return -EINVAL;
121594ff7d0SChristophe Lombard 
122594ff7d0SChristophe Lombard 	buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
123594ff7d0SChristophe Lombard 	if (!buf)
124594ff7d0SChristophe Lombard 		return -ENOMEM;
125594ff7d0SChristophe Lombard 
126594ff7d0SChristophe Lombard 	dn = of_find_node_by_phandle(be32_to_cpu(phandle));
127594ff7d0SChristophe Lombard 	if (!dn) {
128594ff7d0SChristophe Lombard 		kfree(buf);
129594ff7d0SChristophe Lombard 		return -ENOENT;
130594ff7d0SChristophe Lombard 	}
131594ff7d0SChristophe Lombard 
132594ff7d0SChristophe Lombard 	upwa = (struct update_props_workarea *)&buf[0];
133594ff7d0SChristophe Lombard 	upwa->phandle = phandle;
134594ff7d0SChristophe Lombard 	do {
135594ff7d0SChristophe Lombard 		rc = rcall(token, buf, scope);
136594ff7d0SChristophe Lombard 		if (rc < 0)
137594ff7d0SChristophe Lombard 			break;
138594ff7d0SChristophe Lombard 
139594ff7d0SChristophe Lombard 		prop_data = buf + sizeof(*upwa);
140594ff7d0SChristophe Lombard 		nprops = be32_to_cpu(upwa->nprops);
141594ff7d0SChristophe Lombard 
142594ff7d0SChristophe Lombard 		if (*prop_data == 0) {
143594ff7d0SChristophe Lombard 			prop_data++;
144594ff7d0SChristophe Lombard 			vd = be32_to_cpu(*(__be32 *)prop_data);
145594ff7d0SChristophe Lombard 			prop_data += vd + sizeof(vd);
146594ff7d0SChristophe Lombard 			nprops--;
147594ff7d0SChristophe Lombard 		}
148594ff7d0SChristophe Lombard 
149594ff7d0SChristophe Lombard 		for (i = 0; i < nprops; i++) {
150594ff7d0SChristophe Lombard 			char *prop_name;
151594ff7d0SChristophe Lombard 
152594ff7d0SChristophe Lombard 			prop_name = prop_data;
153594ff7d0SChristophe Lombard 			prop_data += strlen(prop_name) + 1;
154594ff7d0SChristophe Lombard 			vd = be32_to_cpu(*(__be32 *)prop_data);
155594ff7d0SChristophe Lombard 			prop_data += sizeof(vd);
156594ff7d0SChristophe Lombard 
157594ff7d0SChristophe Lombard 			if ((vd != 0x00000000) && (vd != 0x80000000)) {
158594ff7d0SChristophe Lombard 				ret = update_property(dn, prop_name, vd,
159594ff7d0SChristophe Lombard 						prop_data);
160594ff7d0SChristophe Lombard 				if (ret)
161594ff7d0SChristophe Lombard 					pr_err("cxl: Could not update property %s - %i\n",
162594ff7d0SChristophe Lombard 					       prop_name, ret);
163594ff7d0SChristophe Lombard 
164594ff7d0SChristophe Lombard 				prop_data += vd;
165594ff7d0SChristophe Lombard 			}
166594ff7d0SChristophe Lombard 		}
167594ff7d0SChristophe Lombard 	} while (rc == 1);
168594ff7d0SChristophe Lombard 
169594ff7d0SChristophe Lombard 	of_node_put(dn);
170594ff7d0SChristophe Lombard 	kfree(buf);
171594ff7d0SChristophe Lombard 	return rc;
172594ff7d0SChristophe Lombard }
173594ff7d0SChristophe Lombard 
update_devicetree(struct cxl * adapter,s32 scope)174594ff7d0SChristophe Lombard static int update_devicetree(struct cxl *adapter, s32 scope)
175594ff7d0SChristophe Lombard {
176594ff7d0SChristophe Lombard 	struct update_nodes_workarea *unwa;
177594ff7d0SChristophe Lombard 	u32 action, node_count;
178594ff7d0SChristophe Lombard 	int token, rc, i;
179017d740eSLee Jones 	__be32 *data, phandle;
180594ff7d0SChristophe Lombard 	char *buf;
181594ff7d0SChristophe Lombard 
182594ff7d0SChristophe Lombard 	token = rtas_token("ibm,update-nodes");
183594ff7d0SChristophe Lombard 	if (token == RTAS_UNKNOWN_SERVICE)
184594ff7d0SChristophe Lombard 		return -EINVAL;
185594ff7d0SChristophe Lombard 
186594ff7d0SChristophe Lombard 	buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
187594ff7d0SChristophe Lombard 	if (!buf)
188594ff7d0SChristophe Lombard 		return -ENOMEM;
189594ff7d0SChristophe Lombard 
190594ff7d0SChristophe Lombard 	unwa = (struct update_nodes_workarea *)&buf[0];
191594ff7d0SChristophe Lombard 	unwa->unit_address = cpu_to_be64(adapter->guest->handle);
192594ff7d0SChristophe Lombard 	do {
193594ff7d0SChristophe Lombard 		rc = rcall(token, buf, scope);
194594ff7d0SChristophe Lombard 		if (rc && rc != 1)
195594ff7d0SChristophe Lombard 			break;
196594ff7d0SChristophe Lombard 
197594ff7d0SChristophe Lombard 		data = (__be32 *)buf + 4;
198594ff7d0SChristophe Lombard 		while (be32_to_cpu(*data) & NODE_ACTION_MASK) {
199594ff7d0SChristophe Lombard 			action = be32_to_cpu(*data) & NODE_ACTION_MASK;
200594ff7d0SChristophe Lombard 			node_count = be32_to_cpu(*data) & NODE_COUNT_MASK;
201594ff7d0SChristophe Lombard 			pr_devel("device reconfiguration - action: %#x, nodes: %#x\n",
202594ff7d0SChristophe Lombard 				 action, node_count);
203594ff7d0SChristophe Lombard 			data++;
204594ff7d0SChristophe Lombard 
205594ff7d0SChristophe Lombard 			for (i = 0; i < node_count; i++) {
206594ff7d0SChristophe Lombard 				phandle = *data++;
207594ff7d0SChristophe Lombard 
208594ff7d0SChristophe Lombard 				switch (action) {
209594ff7d0SChristophe Lombard 				case OPCODE_DELETE:
210594ff7d0SChristophe Lombard 					/* nothing to do */
211594ff7d0SChristophe Lombard 					break;
212594ff7d0SChristophe Lombard 				case OPCODE_UPDATE:
213594ff7d0SChristophe Lombard 					update_node(phandle, scope);
214594ff7d0SChristophe Lombard 					break;
215594ff7d0SChristophe Lombard 				case OPCODE_ADD:
216594ff7d0SChristophe Lombard 					/* nothing to do, just move pointer */
217017d740eSLee Jones 					data++;
218594ff7d0SChristophe Lombard 					break;
219594ff7d0SChristophe Lombard 				}
220594ff7d0SChristophe Lombard 			}
221594ff7d0SChristophe Lombard 		}
222594ff7d0SChristophe Lombard 	} while (rc == 1);
223594ff7d0SChristophe Lombard 
224594ff7d0SChristophe Lombard 	kfree(buf);
225594ff7d0SChristophe Lombard 	return 0;
226594ff7d0SChristophe Lombard }
227594ff7d0SChristophe Lombard 
handle_image(struct cxl * adapter,int operation,long (* fct)(u64,u64,u64,u64 *),struct cxl_adapter_image * ai)228594ff7d0SChristophe Lombard static int handle_image(struct cxl *adapter, int operation,
229594ff7d0SChristophe Lombard 			long (*fct)(u64, u64, u64, u64 *),
230594ff7d0SChristophe Lombard 			struct cxl_adapter_image *ai)
231594ff7d0SChristophe Lombard {
232594ff7d0SChristophe Lombard 	size_t mod, s_copy, len_chunk = 0;
233594ff7d0SChristophe Lombard 	struct ai_header *header = NULL;
234594ff7d0SChristophe Lombard 	unsigned int entries = 0, i;
235594ff7d0SChristophe Lombard 	void *dest, *from;
236594ff7d0SChristophe Lombard 	int rc = 0, need_header;
237594ff7d0SChristophe Lombard 
238594ff7d0SChristophe Lombard 	/* base adapter image header */
239594ff7d0SChristophe Lombard 	need_header = (ai->flags & CXL_AI_NEED_HEADER);
240594ff7d0SChristophe Lombard 	if (need_header) {
241594ff7d0SChristophe Lombard 		header = kzalloc(sizeof(struct ai_header), GFP_KERNEL);
242594ff7d0SChristophe Lombard 		if (!header)
243594ff7d0SChristophe Lombard 			return -ENOMEM;
244594ff7d0SChristophe Lombard 		header->version = cpu_to_be16(1);
245594ff7d0SChristophe Lombard 		header->vendor = cpu_to_be16(adapter->guest->vendor);
246594ff7d0SChristophe Lombard 		header->device = cpu_to_be16(adapter->guest->device);
247594ff7d0SChristophe Lombard 		header->subsystem_vendor = cpu_to_be16(adapter->guest->subsystem_vendor);
248594ff7d0SChristophe Lombard 		header->subsystem = cpu_to_be16(adapter->guest->subsystem);
249594ff7d0SChristophe Lombard 		header->image_offset = cpu_to_be64(CXL_AI_HEADER_SIZE);
250594ff7d0SChristophe Lombard 		header->image_length = cpu_to_be64(ai->len_image);
251594ff7d0SChristophe Lombard 	}
252594ff7d0SChristophe Lombard 
253594ff7d0SChristophe Lombard 	/* number of entries in the list */
254594ff7d0SChristophe Lombard 	len_chunk = ai->len_data;
255594ff7d0SChristophe Lombard 	if (need_header)
256594ff7d0SChristophe Lombard 		len_chunk += CXL_AI_HEADER_SIZE;
257594ff7d0SChristophe Lombard 
258594ff7d0SChristophe Lombard 	entries = len_chunk / CXL_AI_BUFFER_SIZE;
259594ff7d0SChristophe Lombard 	mod = len_chunk % CXL_AI_BUFFER_SIZE;
260594ff7d0SChristophe Lombard 	if (mod)
261594ff7d0SChristophe Lombard 		entries++;
262594ff7d0SChristophe Lombard 
263594ff7d0SChristophe Lombard 	if (entries > CXL_AI_MAX_ENTRIES) {
264594ff7d0SChristophe Lombard 		rc = -EINVAL;
265594ff7d0SChristophe Lombard 		goto err;
266594ff7d0SChristophe Lombard 	}
267594ff7d0SChristophe Lombard 
268594ff7d0SChristophe Lombard 	/*          < -- MAX_CHUNK_SIZE = 4096 * 256 = 1048576 bytes -->
269594ff7d0SChristophe Lombard 	 * chunk 0  ----------------------------------------------------
270594ff7d0SChristophe Lombard 	 *          | header   |  data                                 |
271594ff7d0SChristophe Lombard 	 *          ----------------------------------------------------
272594ff7d0SChristophe Lombard 	 * chunk 1  ----------------------------------------------------
273594ff7d0SChristophe Lombard 	 *          | data                                             |
274594ff7d0SChristophe Lombard 	 *          ----------------------------------------------------
275594ff7d0SChristophe Lombard 	 * ....
276594ff7d0SChristophe Lombard 	 * chunk n  ----------------------------------------------------
277594ff7d0SChristophe Lombard 	 *          | data                                             |
278594ff7d0SChristophe Lombard 	 *          ----------------------------------------------------
279594ff7d0SChristophe Lombard 	 */
280594ff7d0SChristophe Lombard 	from = (void *) ai->data;
281594ff7d0SChristophe Lombard 	for (i = 0; i < entries; i++) {
282594ff7d0SChristophe Lombard 		dest = buffer[i];
283594ff7d0SChristophe Lombard 		s_copy = CXL_AI_BUFFER_SIZE;
284594ff7d0SChristophe Lombard 
285594ff7d0SChristophe Lombard 		if ((need_header) && (i == 0)) {
286594ff7d0SChristophe Lombard 			/* add adapter image header */
287594ff7d0SChristophe Lombard 			memcpy(buffer[i], header, sizeof(struct ai_header));
288594ff7d0SChristophe Lombard 			s_copy = CXL_AI_BUFFER_SIZE - CXL_AI_HEADER_SIZE;
289594ff7d0SChristophe Lombard 			dest += CXL_AI_HEADER_SIZE; /* image offset */
290594ff7d0SChristophe Lombard 		}
291594ff7d0SChristophe Lombard 		if ((i == (entries - 1)) && mod)
292594ff7d0SChristophe Lombard 			s_copy = mod;
293594ff7d0SChristophe Lombard 
294594ff7d0SChristophe Lombard 		/* copy data */
295594ff7d0SChristophe Lombard 		if (copy_from_user(dest, from, s_copy))
296594ff7d0SChristophe Lombard 			goto err;
297594ff7d0SChristophe Lombard 
298594ff7d0SChristophe Lombard 		/* fill in the list */
299594ff7d0SChristophe Lombard 		le[i].phys_addr = cpu_to_be64(virt_to_phys(buffer[i]));
300594ff7d0SChristophe Lombard 		le[i].len = cpu_to_be64(CXL_AI_BUFFER_SIZE);
301594ff7d0SChristophe Lombard 		if ((i == (entries - 1)) && mod)
302594ff7d0SChristophe Lombard 			le[i].len = cpu_to_be64(mod);
303594ff7d0SChristophe Lombard 		from += s_copy;
304594ff7d0SChristophe Lombard 	}
305594ff7d0SChristophe Lombard 	pr_devel("%s (op: %i, need header: %i, entries: %i, token: %#llx)\n",
306594ff7d0SChristophe Lombard 		 __func__, operation, need_header, entries, continue_token);
307594ff7d0SChristophe Lombard 
308594ff7d0SChristophe Lombard 	/*
309594ff7d0SChristophe Lombard 	 * download/validate the adapter image to the coherent
310594ff7d0SChristophe Lombard 	 * platform facility
311594ff7d0SChristophe Lombard 	 */
312594ff7d0SChristophe Lombard 	rc = fct(adapter->guest->handle, virt_to_phys(le), entries,
313594ff7d0SChristophe Lombard 		&continue_token);
314594ff7d0SChristophe Lombard 	if (rc == 0) /* success of download/validation operation */
315594ff7d0SChristophe Lombard 		continue_token = 0;
316594ff7d0SChristophe Lombard 
317594ff7d0SChristophe Lombard err:
318594ff7d0SChristophe Lombard 	kfree(header);
319594ff7d0SChristophe Lombard 
320594ff7d0SChristophe Lombard 	return rc;
321594ff7d0SChristophe Lombard }
322594ff7d0SChristophe Lombard 
transfer_image(struct cxl * adapter,int operation,struct cxl_adapter_image * ai)323594ff7d0SChristophe Lombard static int transfer_image(struct cxl *adapter, int operation,
324594ff7d0SChristophe Lombard 			struct cxl_adapter_image *ai)
325594ff7d0SChristophe Lombard {
326594ff7d0SChristophe Lombard 	int rc = 0;
327594ff7d0SChristophe Lombard 	int afu;
328594ff7d0SChristophe Lombard 
329594ff7d0SChristophe Lombard 	switch (operation) {
330594ff7d0SChristophe Lombard 	case DOWNLOAD_IMAGE:
331594ff7d0SChristophe Lombard 		rc = handle_image(adapter, operation,
332594ff7d0SChristophe Lombard 				&cxl_h_download_adapter_image, ai);
333594ff7d0SChristophe Lombard 		if (rc < 0) {
334594ff7d0SChristophe Lombard 			pr_devel("resetting adapter\n");
335594ff7d0SChristophe Lombard 			cxl_h_reset_adapter(adapter->guest->handle);
336594ff7d0SChristophe Lombard 		}
337594ff7d0SChristophe Lombard 		return rc;
338594ff7d0SChristophe Lombard 
339594ff7d0SChristophe Lombard 	case VALIDATE_IMAGE:
340594ff7d0SChristophe Lombard 		rc = handle_image(adapter, operation,
341594ff7d0SChristophe Lombard 				&cxl_h_validate_adapter_image, ai);
342594ff7d0SChristophe Lombard 		if (rc < 0) {
343594ff7d0SChristophe Lombard 			pr_devel("resetting adapter\n");
344594ff7d0SChristophe Lombard 			cxl_h_reset_adapter(adapter->guest->handle);
345594ff7d0SChristophe Lombard 			return rc;
346594ff7d0SChristophe Lombard 		}
347594ff7d0SChristophe Lombard 		if (rc == 0) {
3480a95160eSMasanari Iida 			pr_devel("remove current afu\n");
349594ff7d0SChristophe Lombard 			for (afu = 0; afu < adapter->slices; afu++)
350594ff7d0SChristophe Lombard 				cxl_guest_remove_afu(adapter->afu[afu]);
351594ff7d0SChristophe Lombard 
352594ff7d0SChristophe Lombard 			pr_devel("resetting adapter\n");
353594ff7d0SChristophe Lombard 			cxl_h_reset_adapter(adapter->guest->handle);
354594ff7d0SChristophe Lombard 
355594ff7d0SChristophe Lombard 			/* The entire image has now been
356594ff7d0SChristophe Lombard 			 * downloaded and the validation has
357594ff7d0SChristophe Lombard 			 * been successfully performed.
358594ff7d0SChristophe Lombard 			 * After that, the partition should call
359594ff7d0SChristophe Lombard 			 * ibm,update-nodes and
360594ff7d0SChristophe Lombard 			 * ibm,update-properties to receive the
361594ff7d0SChristophe Lombard 			 * current configuration
362594ff7d0SChristophe Lombard 			 */
363594ff7d0SChristophe Lombard 			rc = update_devicetree(adapter, DEVICE_SCOPE);
364594ff7d0SChristophe Lombard 			transfer = 1;
365594ff7d0SChristophe Lombard 		}
366594ff7d0SChristophe Lombard 		return rc;
367594ff7d0SChristophe Lombard 	}
368594ff7d0SChristophe Lombard 
369594ff7d0SChristophe Lombard 	return -EINVAL;
370594ff7d0SChristophe Lombard }
371594ff7d0SChristophe Lombard 
ioctl_transfer_image(struct cxl * adapter,int operation,struct cxl_adapter_image __user * uai)372594ff7d0SChristophe Lombard static long ioctl_transfer_image(struct cxl *adapter, int operation,
373594ff7d0SChristophe Lombard 				struct cxl_adapter_image __user *uai)
374594ff7d0SChristophe Lombard {
375594ff7d0SChristophe Lombard 	struct cxl_adapter_image ai;
376594ff7d0SChristophe Lombard 
377594ff7d0SChristophe Lombard 	pr_devel("%s\n", __func__);
378594ff7d0SChristophe Lombard 
379594ff7d0SChristophe Lombard 	if (copy_from_user(&ai, uai, sizeof(struct cxl_adapter_image)))
380594ff7d0SChristophe Lombard 		return -EFAULT;
381594ff7d0SChristophe Lombard 
382594ff7d0SChristophe Lombard 	/*
383594ff7d0SChristophe Lombard 	 * Make sure reserved fields and bits are set to 0
384594ff7d0SChristophe Lombard 	 */
385594ff7d0SChristophe Lombard 	if (ai.reserved1 || ai.reserved2 || ai.reserved3 || ai.reserved4 ||
386594ff7d0SChristophe Lombard 		(ai.flags & ~CXL_AI_ALL))
387594ff7d0SChristophe Lombard 		return -EINVAL;
388594ff7d0SChristophe Lombard 
389594ff7d0SChristophe Lombard 	return transfer_image(adapter, operation, &ai);
390594ff7d0SChristophe Lombard }
391594ff7d0SChristophe Lombard 
device_open(struct inode * inode,struct file * file)392594ff7d0SChristophe Lombard static int device_open(struct inode *inode, struct file *file)
393594ff7d0SChristophe Lombard {
394594ff7d0SChristophe Lombard 	int adapter_num = CXL_DEVT_ADAPTER(inode->i_rdev);
395594ff7d0SChristophe Lombard 	struct cxl *adapter;
396594ff7d0SChristophe Lombard 	int rc = 0, i;
397594ff7d0SChristophe Lombard 
398594ff7d0SChristophe Lombard 	pr_devel("in %s\n", __func__);
399594ff7d0SChristophe Lombard 
400594ff7d0SChristophe Lombard 	BUG_ON(sizeof(struct ai_header) != CXL_AI_HEADER_SIZE);
401594ff7d0SChristophe Lombard 
402594ff7d0SChristophe Lombard 	/* Allows one process to open the device by using a semaphore */
403594ff7d0SChristophe Lombard 	if (down_interruptible(&sem) != 0)
404594ff7d0SChristophe Lombard 		return -EPERM;
405594ff7d0SChristophe Lombard 
40658d876faSDan Carpenter 	if (!(adapter = get_cxl_adapter(adapter_num))) {
40758d876faSDan Carpenter 		rc = -ENODEV;
40858d876faSDan Carpenter 		goto err_unlock;
40958d876faSDan Carpenter 	}
410594ff7d0SChristophe Lombard 
411594ff7d0SChristophe Lombard 	file->private_data = adapter;
412594ff7d0SChristophe Lombard 	continue_token = 0;
413594ff7d0SChristophe Lombard 	transfer = 0;
414594ff7d0SChristophe Lombard 
415594ff7d0SChristophe Lombard 	for (i = 0; i < CXL_AI_MAX_ENTRIES; i++)
416594ff7d0SChristophe Lombard 		buffer[i] = NULL;
417594ff7d0SChristophe Lombard 
418594ff7d0SChristophe Lombard 	/* aligned buffer containing list entries which describes up to
419594ff7d0SChristophe Lombard 	 * 1 megabyte of data (256 entries of 4096 bytes each)
420594ff7d0SChristophe Lombard 	 *  Logical real address of buffer 0  -  Buffer 0 length in bytes
421594ff7d0SChristophe Lombard 	 *  Logical real address of buffer 1  -  Buffer 1 length in bytes
422594ff7d0SChristophe Lombard 	 *  Logical real address of buffer 2  -  Buffer 2 length in bytes
423594ff7d0SChristophe Lombard 	 *  ....
424594ff7d0SChristophe Lombard 	 *  ....
425594ff7d0SChristophe Lombard 	 *  Logical real address of buffer N  -  Buffer N length in bytes
426594ff7d0SChristophe Lombard 	 */
427594ff7d0SChristophe Lombard 	le = (struct sg_list *)get_zeroed_page(GFP_KERNEL);
428594ff7d0SChristophe Lombard 	if (!le) {
429594ff7d0SChristophe Lombard 		rc = -ENOMEM;
430594ff7d0SChristophe Lombard 		goto err;
431594ff7d0SChristophe Lombard 	}
432594ff7d0SChristophe Lombard 
433594ff7d0SChristophe Lombard 	for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) {
434594ff7d0SChristophe Lombard 		buffer[i] = (unsigned long *)get_zeroed_page(GFP_KERNEL);
435594ff7d0SChristophe Lombard 		if (!buffer[i]) {
436594ff7d0SChristophe Lombard 			rc = -ENOMEM;
437594ff7d0SChristophe Lombard 			goto err1;
438594ff7d0SChristophe Lombard 		}
439594ff7d0SChristophe Lombard 	}
440594ff7d0SChristophe Lombard 
441594ff7d0SChristophe Lombard 	return 0;
442594ff7d0SChristophe Lombard 
443594ff7d0SChristophe Lombard err1:
444594ff7d0SChristophe Lombard 	for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) {
445594ff7d0SChristophe Lombard 		if (buffer[i])
446594ff7d0SChristophe Lombard 			free_page((unsigned long) buffer[i]);
447594ff7d0SChristophe Lombard 	}
448594ff7d0SChristophe Lombard 
449594ff7d0SChristophe Lombard 	if (le)
450594ff7d0SChristophe Lombard 		free_page((unsigned long) le);
451594ff7d0SChristophe Lombard err:
452594ff7d0SChristophe Lombard 	put_device(&adapter->dev);
45358d876faSDan Carpenter err_unlock:
45458d876faSDan Carpenter 	up(&sem);
455594ff7d0SChristophe Lombard 
456594ff7d0SChristophe Lombard 	return rc;
457594ff7d0SChristophe Lombard }
458594ff7d0SChristophe Lombard 
device_ioctl(struct file * file,unsigned int cmd,unsigned long arg)459594ff7d0SChristophe Lombard static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
460594ff7d0SChristophe Lombard {
461594ff7d0SChristophe Lombard 	struct cxl *adapter = file->private_data;
462594ff7d0SChristophe Lombard 
463594ff7d0SChristophe Lombard 	pr_devel("in %s\n", __func__);
464594ff7d0SChristophe Lombard 
465594ff7d0SChristophe Lombard 	if (cmd == CXL_IOCTL_DOWNLOAD_IMAGE)
466594ff7d0SChristophe Lombard 		return ioctl_transfer_image(adapter,
467594ff7d0SChristophe Lombard 					DOWNLOAD_IMAGE,
468594ff7d0SChristophe Lombard 					(struct cxl_adapter_image __user *)arg);
469594ff7d0SChristophe Lombard 	else if (cmd == CXL_IOCTL_VALIDATE_IMAGE)
470594ff7d0SChristophe Lombard 		return ioctl_transfer_image(adapter,
471594ff7d0SChristophe Lombard 					VALIDATE_IMAGE,
472594ff7d0SChristophe Lombard 					(struct cxl_adapter_image __user *)arg);
473594ff7d0SChristophe Lombard 	else
474594ff7d0SChristophe Lombard 		return -EINVAL;
475594ff7d0SChristophe Lombard }
476594ff7d0SChristophe Lombard 
device_close(struct inode * inode,struct file * file)477594ff7d0SChristophe Lombard static int device_close(struct inode *inode, struct file *file)
478594ff7d0SChristophe Lombard {
479594ff7d0SChristophe Lombard 	struct cxl *adapter = file->private_data;
480594ff7d0SChristophe Lombard 	int i;
481594ff7d0SChristophe Lombard 
482594ff7d0SChristophe Lombard 	pr_devel("in %s\n", __func__);
483594ff7d0SChristophe Lombard 
484594ff7d0SChristophe Lombard 	for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) {
485594ff7d0SChristophe Lombard 		if (buffer[i])
486594ff7d0SChristophe Lombard 			free_page((unsigned long) buffer[i]);
487594ff7d0SChristophe Lombard 	}
488594ff7d0SChristophe Lombard 
489594ff7d0SChristophe Lombard 	if (le)
490594ff7d0SChristophe Lombard 		free_page((unsigned long) le);
491594ff7d0SChristophe Lombard 
492594ff7d0SChristophe Lombard 	up(&sem);
493594ff7d0SChristophe Lombard 	put_device(&adapter->dev);
494594ff7d0SChristophe Lombard 	continue_token = 0;
495594ff7d0SChristophe Lombard 
496594ff7d0SChristophe Lombard 	/* reload the module */
497594ff7d0SChristophe Lombard 	if (transfer)
498594ff7d0SChristophe Lombard 		cxl_guest_reload_module(adapter);
499594ff7d0SChristophe Lombard 	else {
500594ff7d0SChristophe Lombard 		pr_devel("resetting adapter\n");
501594ff7d0SChristophe Lombard 		cxl_h_reset_adapter(adapter->guest->handle);
502594ff7d0SChristophe Lombard 	}
503594ff7d0SChristophe Lombard 
504594ff7d0SChristophe Lombard 	transfer = 0;
505594ff7d0SChristophe Lombard 	return 0;
506594ff7d0SChristophe Lombard }
507594ff7d0SChristophe Lombard 
508594ff7d0SChristophe Lombard static const struct file_operations fops = {
509594ff7d0SChristophe Lombard 	.owner		= THIS_MODULE,
510594ff7d0SChristophe Lombard 	.open		= device_open,
511594ff7d0SChristophe Lombard 	.unlocked_ioctl	= device_ioctl,
51201b8bca8SArnd Bergmann 	.compat_ioctl	= compat_ptr_ioctl,
513594ff7d0SChristophe Lombard 	.release	= device_close,
514594ff7d0SChristophe Lombard };
515594ff7d0SChristophe Lombard 
cxl_guest_remove_chardev(struct cxl * adapter)516594ff7d0SChristophe Lombard void cxl_guest_remove_chardev(struct cxl *adapter)
517594ff7d0SChristophe Lombard {
518594ff7d0SChristophe Lombard 	cdev_del(&adapter->guest->cdev);
519594ff7d0SChristophe Lombard }
520594ff7d0SChristophe Lombard 
cxl_guest_add_chardev(struct cxl * adapter)521594ff7d0SChristophe Lombard int cxl_guest_add_chardev(struct cxl *adapter)
522594ff7d0SChristophe Lombard {
523594ff7d0SChristophe Lombard 	dev_t devt;
524594ff7d0SChristophe Lombard 	int rc;
525594ff7d0SChristophe Lombard 
526594ff7d0SChristophe Lombard 	devt = MKDEV(MAJOR(cxl_get_dev()), CXL_CARD_MINOR(adapter));
527594ff7d0SChristophe Lombard 	cdev_init(&adapter->guest->cdev, &fops);
528594ff7d0SChristophe Lombard 	if ((rc = cdev_add(&adapter->guest->cdev, devt, 1))) {
529594ff7d0SChristophe Lombard 		dev_err(&adapter->dev,
530594ff7d0SChristophe Lombard 			"Unable to add chardev on adapter (card%i): %i\n",
531594ff7d0SChristophe Lombard 			adapter->adapter_num, rc);
532594ff7d0SChristophe Lombard 		goto err;
533594ff7d0SChristophe Lombard 	}
534594ff7d0SChristophe Lombard 	adapter->dev.devt = devt;
535594ff7d0SChristophe Lombard 	sema_init(&sem, 1);
536594ff7d0SChristophe Lombard err:
537594ff7d0SChristophe Lombard 	return rc;
538594ff7d0SChristophe Lombard }
539