1308ee87aSSven Van Asbroeck // SPDX-License-Identifier: GPL-2.0
2308ee87aSSven Van Asbroeck /*
3308ee87aSSven Van Asbroeck  * HMS Anybus-S Host Driver
4308ee87aSSven Van Asbroeck  *
5308ee87aSSven Van Asbroeck  * Copyright (C) 2018 Arcx Inc
6308ee87aSSven Van Asbroeck  */
7308ee87aSSven Van Asbroeck 
8308ee87aSSven Van Asbroeck /*
9308ee87aSSven Van Asbroeck  * Architecture Overview
10308ee87aSSven Van Asbroeck  * =====================
11308ee87aSSven Van Asbroeck  * This driver (running on the CPU/SoC) and the Anybus-S card communicate
12308ee87aSSven Van Asbroeck  * by reading and writing data to/from the Anybus-S Dual-Port RAM (dpram).
13308ee87aSSven Van Asbroeck  * This is memory connected to both the SoC and Anybus-S card, which both sides
14308ee87aSSven Van Asbroeck  * can access freely and concurrently.
15308ee87aSSven Van Asbroeck  *
16308ee87aSSven Van Asbroeck  * Synchronization happens by means of two registers located in the dpram:
17308ee87aSSven Van Asbroeck  * IND_AB: written exclusively by the Anybus card; and
18308ee87aSSven Van Asbroeck  * IND_AP: written exclusively by this driver.
19308ee87aSSven Van Asbroeck  *
20308ee87aSSven Van Asbroeck  * Communication happens using one of the following mechanisms:
21308ee87aSSven Van Asbroeck  * 1. reserve, read/write, release dpram memory areas:
22308ee87aSSven Van Asbroeck  *	using an IND_AB/IND_AP protocol, the driver is able to reserve certain
23308ee87aSSven Van Asbroeck  *	memory areas. no dpram memory can be read or written except if reserved.
24308ee87aSSven Van Asbroeck  *	(with a few limited exceptions)
25308ee87aSSven Van Asbroeck  * 2. send and receive data structures via a shared mailbox:
26308ee87aSSven Van Asbroeck  *	using an IND_AB/IND_AP protocol, the driver and Anybus card are able to
27308ee87aSSven Van Asbroeck  *	exchange commands and responses using a shared mailbox.
28308ee87aSSven Van Asbroeck  * 3. receive software interrupts:
29308ee87aSSven Van Asbroeck  *	using an IND_AB/IND_AP protocol, the Anybus card is able to notify the
30308ee87aSSven Van Asbroeck  *	driver of certain events such as: bus online/offline, data available.
31308ee87aSSven Van Asbroeck  *	note that software interrupt event bits are located in a memory area
32308ee87aSSven Van Asbroeck  *	which must be reserved before it can be accessed.
33308ee87aSSven Van Asbroeck  *
34308ee87aSSven Van Asbroeck  * The manual[1] is silent on whether these mechanisms can happen concurrently,
35308ee87aSSven Van Asbroeck  * or how they should be synchronized. However, section 13 (Driver Example)
36308ee87aSSven Van Asbroeck  * provides the following suggestion for developing a driver:
37308ee87aSSven Van Asbroeck  * a) an interrupt handler which updates global variables;
38308ee87aSSven Van Asbroeck  * b) a continuously-running task handling area requests (1 above)
39308ee87aSSven Van Asbroeck  * c) a continuously-running task handling mailbox requests (2 above)
40308ee87aSSven Van Asbroeck  * The example conspicuously leaves out software interrupts (3 above), which
41308ee87aSSven Van Asbroeck  * is the thorniest issue to get right (see below).
42308ee87aSSven Van Asbroeck  *
43308ee87aSSven Van Asbroeck  * The naive, straightforward way to implement this would be:
44308ee87aSSven Van Asbroeck  * - create an isr which updates shared variables;
45308ee87aSSven Van Asbroeck  * - create a work_struct which handles software interrupts on a queue;
46308ee87aSSven Van Asbroeck  * - create a function which does reserve/update/unlock in a loop;
47308ee87aSSven Van Asbroeck  * - create a function which does mailbox send/receive in a loop;
48308ee87aSSven Van Asbroeck  * - call the above functions from the driver's read/write/ioctl;
49308ee87aSSven Van Asbroeck  * - synchronize using mutexes/spinlocks:
50308ee87aSSven Van Asbroeck  *	+ only one area request at a time
51308ee87aSSven Van Asbroeck  *	+ only one mailbox request at a time
52308ee87aSSven Van Asbroeck  *	+ protect AB_IND, AB_IND against data hazards (e.g. read-after-write)
53308ee87aSSven Van Asbroeck  *
54308ee87aSSven Van Asbroeck  * Unfortunately, the presence of the software interrupt causes subtle yet
55308ee87aSSven Van Asbroeck  * considerable synchronization issues; especially problematic is the
56308ee87aSSven Van Asbroeck  * requirement to reserve/release the area which contains the status bits.
57308ee87aSSven Van Asbroeck  *
58308ee87aSSven Van Asbroeck  * The driver architecture presented here sidesteps these synchronization issues
59308ee87aSSven Van Asbroeck  * by accessing the dpram from a single kernel thread only. User-space throws
60308ee87aSSven Van Asbroeck  * "tasks" (i.e. 1, 2 above) into a task queue, waits for their completion,
61308ee87aSSven Van Asbroeck  * and the kernel thread runs them to completion.
62308ee87aSSven Van Asbroeck  *
63308ee87aSSven Van Asbroeck  * Each task has a task_function, which is called/run by the queue thread.
64308ee87aSSven Van Asbroeck  * That function communicates with the Anybus card, and returns either
65308ee87aSSven Van Asbroeck  * 0 (OK), a negative error code (error), or -EINPROGRESS (waiting).
66308ee87aSSven Van Asbroeck  * On OK or error, the queue thread completes and dequeues the task,
67308ee87aSSven Van Asbroeck  * which also releases the user space thread which may still be waiting for it.
68308ee87aSSven Van Asbroeck  * On -EINPROGRESS (waiting), the queue thread will leave the task on the queue,
69308ee87aSSven Van Asbroeck  * and revisit (call again) whenever an interrupt event comes in.
70308ee87aSSven Van Asbroeck  *
71308ee87aSSven Van Asbroeck  * Each task has a state machine, which is run by calling its task_function.
72308ee87aSSven Van Asbroeck  * It ensures that the task will go through its various stages over time,
73308ee87aSSven Van Asbroeck  * returning -EINPROGRESS if it wants to wait for an event to happen.
74308ee87aSSven Van Asbroeck  *
75308ee87aSSven Van Asbroeck  * Note that according to the manual's driver example, the following operations
76308ee87aSSven Van Asbroeck  * may run independent of each other:
77308ee87aSSven Van Asbroeck  * - area reserve/read/write/release	(point 1 above)
78308ee87aSSven Van Asbroeck  * - mailbox operations			(point 2 above)
79308ee87aSSven Van Asbroeck  * - switching power on/off
80308ee87aSSven Van Asbroeck  *
81308ee87aSSven Van Asbroeck  * To allow them to run independently, each operation class gets its own queue.
82308ee87aSSven Van Asbroeck  *
83308ee87aSSven Van Asbroeck  * Userspace processes A, B, C, D post tasks to the appropriate queue,
84308ee87aSSven Van Asbroeck  * and wait for task completion:
85308ee87aSSven Van Asbroeck  *
86308ee87aSSven Van Asbroeck  *	process A	B	C	D
87308ee87aSSven Van Asbroeck  *		|	|	|	|
88308ee87aSSven Van Asbroeck  *		v	v	v	v
89308ee87aSSven Van Asbroeck  *	|<-----	========================================
90308ee87aSSven Van Asbroeck  *	|		|	   |		|
91308ee87aSSven Van Asbroeck  *	|		v	   v		v-------<-------+
92308ee87aSSven Van Asbroeck  *	|	+--------------------------------------+	|
93308ee87aSSven Van Asbroeck  *	|	| power q     | mbox q    | area q     |	|
94308ee87aSSven Van Asbroeck  *	|	|------------|------------|------------|	|
95308ee87aSSven Van Asbroeck  *	|	| task       | task       | task       |	|
96308ee87aSSven Van Asbroeck  *	|	| task       | task       | task       |	|
97308ee87aSSven Van Asbroeck  *	|	| task wait  | task wait  | task wait  |	|
98308ee87aSSven Van Asbroeck  *	|	+--------------------------------------+	|
99308ee87aSSven Van Asbroeck  *	|		^	   ^		^		|
100308ee87aSSven Van Asbroeck  *	|		|	   |		|		^
101308ee87aSSven Van Asbroeck  *	|	+--------------------------------------+	|
102308ee87aSSven Van Asbroeck  *	|	|	     queue thread	       |	|
103308ee87aSSven Van Asbroeck  *	|	|--------------------------------------|	|
104308ee87aSSven Van Asbroeck  *	|	| single-threaded:		       |	|
105308ee87aSSven Van Asbroeck  *	|	| loop:				       |	|
106308ee87aSSven Van Asbroeck  *	v	|   for each queue:		       |	|
107308ee87aSSven Van Asbroeck  *	|	|     run task state machine	       |	|
108308ee87aSSven Van Asbroeck  *	|	|     if task waiting:		       |	|
109308ee87aSSven Van Asbroeck  *	|	|       leave on queue		       |	|
110308ee87aSSven Van Asbroeck  *	|	|     if task done:		       |	|
111308ee87aSSven Van Asbroeck  *	|	|       complete task, remove from q   |	|
112308ee87aSSven Van Asbroeck  *	|	|   if software irq event bits set:    |	|
113308ee87aSSven Van Asbroeck  *	|	|     notify userspace		       |	|
114308ee87aSSven Van Asbroeck  *	|	|     post clear event bits task------>|>-------+
115308ee87aSSven Van Asbroeck  *	|	|   wait for IND_AB changed event OR   |
116308ee87aSSven Van Asbroeck  *	|	|            task added event	  OR   |
117308ee87aSSven Van Asbroeck  *	|	|	     timeout		       |
118308ee87aSSven Van Asbroeck  *	|	| end loop			       |
119308ee87aSSven Van Asbroeck  *	|	+--------------------------------------+
120308ee87aSSven Van Asbroeck  *	|	+		wake up		       +
121308ee87aSSven Van Asbroeck  *	|	+--------------------------------------+
122308ee87aSSven Van Asbroeck  *	|		^			^
123308ee87aSSven Van Asbroeck  *	|		|			|
124308ee87aSSven Van Asbroeck  *	+-------->-------			|
125308ee87aSSven Van Asbroeck  *						|
126308ee87aSSven Van Asbroeck  *		+--------------------------------------+
127308ee87aSSven Van Asbroeck  *		|	interrupt service routine      |
128308ee87aSSven Van Asbroeck  *		|--------------------------------------|
129308ee87aSSven Van Asbroeck  *		| wake up queue thread on IND_AB change|
130308ee87aSSven Van Asbroeck  *		+--------------------------------------+
131308ee87aSSven Van Asbroeck  *
132308ee87aSSven Van Asbroeck  * Note that the Anybus interrupt is dual-purpose:
133308ee87aSSven Van Asbroeck  * - after a reset, triggered when the card becomes ready;
134308ee87aSSven Van Asbroeck  * - during normal operation, triggered when AB_IND changes.
135308ee87aSSven Van Asbroeck  * This is why the interrupt service routine doesn't just wake up the
136308ee87aSSven Van Asbroeck  * queue thread, but also completes the card_boot completion.
137308ee87aSSven Van Asbroeck  *
138308ee87aSSven Van Asbroeck  * [1] https://www.anybus.com/docs/librariesprovider7/default-document-library/
139308ee87aSSven Van Asbroeck  *	manuals-design-guides/hms-hmsi-27-275.pdf
140308ee87aSSven Van Asbroeck  */
141308ee87aSSven Van Asbroeck 
142308ee87aSSven Van Asbroeck #include <linux/kernel.h>
143308ee87aSSven Van Asbroeck #include <linux/module.h>
144308ee87aSSven Van Asbroeck #include <linux/init.h>
145308ee87aSSven Van Asbroeck #include <linux/slab.h>
146308ee87aSSven Van Asbroeck #include <linux/interrupt.h>
147308ee87aSSven Van Asbroeck #include <linux/atomic.h>
148308ee87aSSven Van Asbroeck #include <linux/kthread.h>
149308ee87aSSven Van Asbroeck #include <linux/kfifo.h>
150308ee87aSSven Van Asbroeck #include <linux/spinlock.h>
151308ee87aSSven Van Asbroeck #include <linux/uaccess.h>
152308ee87aSSven Van Asbroeck #include <linux/regmap.h>
153308ee87aSSven Van Asbroeck #include <linux/of.h>
154308ee87aSSven Van Asbroeck #include <linux/random.h>
155308ee87aSSven Van Asbroeck #include <linux/kref.h>
156308ee87aSSven Van Asbroeck #include <linux/of_address.h>
157308ee87aSSven Van Asbroeck 
158308ee87aSSven Van Asbroeck /* move to <linux/anybuss-*.h> when taking this out of staging */
159308ee87aSSven Van Asbroeck #include "anybuss-client.h"
160308ee87aSSven Van Asbroeck #include "anybuss-controller.h"
161308ee87aSSven Van Asbroeck 
162308ee87aSSven Van Asbroeck #define DPRAM_SIZE		0x800
163308ee87aSSven Van Asbroeck #define MAX_MBOX_MSG_SZ		0x0FF
164308ee87aSSven Van Asbroeck #define TIMEOUT			(HZ * 2)
165308ee87aSSven Van Asbroeck #define MAX_DATA_AREA_SZ	0x200
166308ee87aSSven Van Asbroeck #define MAX_FBCTRL_AREA_SZ	0x1BE
167308ee87aSSven Van Asbroeck 
168308ee87aSSven Van Asbroeck #define REG_BOOTLOADER_V	0x7C0
169308ee87aSSven Van Asbroeck #define REG_API_V		0x7C2
170308ee87aSSven Van Asbroeck #define REG_FIELDBUS_V		0x7C4
171308ee87aSSven Van Asbroeck #define REG_SERIAL_NO		0x7C6
172308ee87aSSven Van Asbroeck #define REG_FIELDBUS_TYPE	0x7CC
173308ee87aSSven Van Asbroeck #define REG_MODULE_SW_V		0x7CE
174308ee87aSSven Van Asbroeck #define REG_IND_AB		0x7FF
175308ee87aSSven Van Asbroeck #define REG_IND_AP		0x7FE
176308ee87aSSven Van Asbroeck #define REG_EVENT_CAUSE		0x7ED
177308ee87aSSven Van Asbroeck #define MBOX_IN_AREA		0x400
178308ee87aSSven Van Asbroeck #define MBOX_OUT_AREA		0x520
179308ee87aSSven Van Asbroeck #define DATA_IN_AREA		0x000
180308ee87aSSven Van Asbroeck #define DATA_OUT_AREA		0x200
181308ee87aSSven Van Asbroeck #define FBCTRL_AREA		0x640
182308ee87aSSven Van Asbroeck 
183308ee87aSSven Van Asbroeck #define EVENT_CAUSE_DC          0x01
184308ee87aSSven Van Asbroeck #define EVENT_CAUSE_FBOF        0x02
185308ee87aSSven Van Asbroeck #define EVENT_CAUSE_FBON        0x04
186308ee87aSSven Van Asbroeck 
187308ee87aSSven Van Asbroeck #define IND_AB_UPDATED		0x08
188308ee87aSSven Van Asbroeck #define IND_AX_MIN		0x80
189308ee87aSSven Van Asbroeck #define IND_AX_MOUT		0x40
190308ee87aSSven Van Asbroeck #define IND_AX_IN		0x04
191308ee87aSSven Van Asbroeck #define IND_AX_OUT		0x02
192308ee87aSSven Van Asbroeck #define IND_AX_FBCTRL		0x01
193308ee87aSSven Van Asbroeck #define IND_AP_LOCK		0x08
194308ee87aSSven Van Asbroeck #define IND_AP_ACTION		0x10
195308ee87aSSven Van Asbroeck #define IND_AX_EVNT		0x20
196308ee87aSSven Van Asbroeck #define IND_AP_ABITS		(IND_AX_IN | IND_AX_OUT | \
197308ee87aSSven Van Asbroeck 					IND_AX_FBCTRL | \
198308ee87aSSven Van Asbroeck 					IND_AP_ACTION | IND_AP_LOCK)
199308ee87aSSven Van Asbroeck 
200308ee87aSSven Van Asbroeck #define INFO_TYPE_FB		0x0002
201308ee87aSSven Van Asbroeck #define INFO_TYPE_APP		0x0001
202308ee87aSSven Van Asbroeck #define INFO_COMMAND		0x4000
203308ee87aSSven Van Asbroeck 
204308ee87aSSven Van Asbroeck #define OP_MODE_FBFC		0x0002
205308ee87aSSven Van Asbroeck #define OP_MODE_FBS		0x0004
206308ee87aSSven Van Asbroeck #define OP_MODE_CD		0x0200
207308ee87aSSven Van Asbroeck 
208308ee87aSSven Van Asbroeck #define CMD_START_INIT		0x0001
209308ee87aSSven Van Asbroeck #define CMD_ANYBUS_INIT		0x0002
210308ee87aSSven Van Asbroeck #define CMD_END_INIT		0x0003
211308ee87aSSven Van Asbroeck 
212308ee87aSSven Van Asbroeck /*
213308ee87aSSven Van Asbroeck  * ---------------------------------------------------------------
214308ee87aSSven Van Asbroeck  * Anybus mailbox messages - definitions
215308ee87aSSven Van Asbroeck  * ---------------------------------------------------------------
216308ee87aSSven Van Asbroeck  * note that we're depending on the layout of these structures being
217308ee87aSSven Van Asbroeck  * exactly as advertised.
218308ee87aSSven Van Asbroeck  */
219308ee87aSSven Van Asbroeck 
220308ee87aSSven Van Asbroeck struct anybus_mbox_hdr {
221308ee87aSSven Van Asbroeck 	__be16 id;
222308ee87aSSven Van Asbroeck 	__be16 info;
223308ee87aSSven Van Asbroeck 	__be16 cmd_num;
224308ee87aSSven Van Asbroeck 	__be16 data_size;
225308ee87aSSven Van Asbroeck 	__be16 frame_count;
226308ee87aSSven Van Asbroeck 	__be16 frame_num;
227308ee87aSSven Van Asbroeck 	__be16 offset_high;
228308ee87aSSven Van Asbroeck 	__be16 offset_low;
229308ee87aSSven Van Asbroeck 	__be16 extended[8];
230308ee87aSSven Van Asbroeck };
231308ee87aSSven Van Asbroeck 
232308ee87aSSven Van Asbroeck struct msg_anybus_init {
233308ee87aSSven Van Asbroeck 	__be16 input_io_len;
234308ee87aSSven Van Asbroeck 	__be16 input_dpram_len;
235308ee87aSSven Van Asbroeck 	__be16 input_total_len;
236308ee87aSSven Van Asbroeck 	__be16 output_io_len;
237308ee87aSSven Van Asbroeck 	__be16 output_dpram_len;
238308ee87aSSven Van Asbroeck 	__be16 output_total_len;
239308ee87aSSven Van Asbroeck 	__be16 op_mode;
240308ee87aSSven Van Asbroeck 	__be16 notif_config;
241308ee87aSSven Van Asbroeck 	__be16 wd_val;
242308ee87aSSven Van Asbroeck };
243308ee87aSSven Van Asbroeck 
244308ee87aSSven Van Asbroeck /* ------------- ref counted tasks ------------- */
245308ee87aSSven Van Asbroeck 
246308ee87aSSven Van Asbroeck struct ab_task;
247308ee87aSSven Van Asbroeck typedef int (*ab_task_fn_t)(struct anybuss_host *cd,
248308ee87aSSven Van Asbroeck 					struct ab_task *t);
249308ee87aSSven Van Asbroeck typedef void (*ab_done_fn_t)(struct anybuss_host *cd);
250308ee87aSSven Van Asbroeck 
251308ee87aSSven Van Asbroeck struct area_priv {
252308ee87aSSven Van Asbroeck 	bool is_write;
253308ee87aSSven Van Asbroeck 	u16 flags;
254308ee87aSSven Van Asbroeck 	u16 addr;
255308ee87aSSven Van Asbroeck 	size_t count;
256308ee87aSSven Van Asbroeck 	u8 buf[MAX_DATA_AREA_SZ];
257308ee87aSSven Van Asbroeck };
258308ee87aSSven Van Asbroeck 
259308ee87aSSven Van Asbroeck struct mbox_priv {
260308ee87aSSven Van Asbroeck 	struct anybus_mbox_hdr hdr;
261308ee87aSSven Van Asbroeck 	size_t msg_out_sz;
262308ee87aSSven Van Asbroeck 	size_t msg_in_sz;
263308ee87aSSven Van Asbroeck 	u8 msg[MAX_MBOX_MSG_SZ];
264308ee87aSSven Van Asbroeck };
265308ee87aSSven Van Asbroeck 
266308ee87aSSven Van Asbroeck struct ab_task {
267308ee87aSSven Van Asbroeck 	struct kmem_cache	*cache;
268308ee87aSSven Van Asbroeck 	struct kref		refcount;
269308ee87aSSven Van Asbroeck 	ab_task_fn_t		task_fn;
270308ee87aSSven Van Asbroeck 	ab_done_fn_t		done_fn;
271308ee87aSSven Van Asbroeck 	int			result;
272308ee87aSSven Van Asbroeck 	struct completion	done;
273308ee87aSSven Van Asbroeck 	unsigned long		start_jiffies;
274308ee87aSSven Van Asbroeck 	union {
275308ee87aSSven Van Asbroeck 		struct area_priv area_pd;
276308ee87aSSven Van Asbroeck 		struct mbox_priv mbox_pd;
277308ee87aSSven Van Asbroeck 	};
278308ee87aSSven Van Asbroeck };
279308ee87aSSven Van Asbroeck 
ab_task_create_get(struct kmem_cache * cache,ab_task_fn_t task_fn)280308ee87aSSven Van Asbroeck static struct ab_task *ab_task_create_get(struct kmem_cache *cache,
281308ee87aSSven Van Asbroeck 					  ab_task_fn_t task_fn)
282308ee87aSSven Van Asbroeck {
283308ee87aSSven Van Asbroeck 	struct ab_task *t;
284308ee87aSSven Van Asbroeck 
285308ee87aSSven Van Asbroeck 	t = kmem_cache_alloc(cache, GFP_KERNEL);
286308ee87aSSven Van Asbroeck 	if (!t)
287308ee87aSSven Van Asbroeck 		return NULL;
288308ee87aSSven Van Asbroeck 	t->cache = cache;
289308ee87aSSven Van Asbroeck 	kref_init(&t->refcount);
290308ee87aSSven Van Asbroeck 	t->task_fn = task_fn;
291308ee87aSSven Van Asbroeck 	t->done_fn = NULL;
292308ee87aSSven Van Asbroeck 	t->result = 0;
293308ee87aSSven Van Asbroeck 	init_completion(&t->done);
294308ee87aSSven Van Asbroeck 	return t;
295308ee87aSSven Van Asbroeck }
296308ee87aSSven Van Asbroeck 
__ab_task_destroy(struct kref * refcount)297308ee87aSSven Van Asbroeck static void __ab_task_destroy(struct kref *refcount)
298308ee87aSSven Van Asbroeck {
299308ee87aSSven Van Asbroeck 	struct ab_task *t = container_of(refcount, struct ab_task, refcount);
300308ee87aSSven Van Asbroeck 	struct kmem_cache *cache = t->cache;
301308ee87aSSven Van Asbroeck 
302308ee87aSSven Van Asbroeck 	kmem_cache_free(cache, t);
303308ee87aSSven Van Asbroeck }
304308ee87aSSven Van Asbroeck 
ab_task_put(struct ab_task * t)305308ee87aSSven Van Asbroeck static void ab_task_put(struct ab_task *t)
306308ee87aSSven Van Asbroeck {
307308ee87aSSven Van Asbroeck 	kref_put(&t->refcount, __ab_task_destroy);
308308ee87aSSven Van Asbroeck }
309308ee87aSSven Van Asbroeck 
__ab_task_get(struct ab_task * t)310308ee87aSSven Van Asbroeck static struct ab_task *__ab_task_get(struct ab_task *t)
311308ee87aSSven Van Asbroeck {
312308ee87aSSven Van Asbroeck 	kref_get(&t->refcount);
313308ee87aSSven Van Asbroeck 	return t;
314308ee87aSSven Van Asbroeck }
315308ee87aSSven Van Asbroeck 
__ab_task_finish(struct ab_task * t,struct anybuss_host * cd)316308ee87aSSven Van Asbroeck static void __ab_task_finish(struct ab_task *t, struct anybuss_host *cd)
317308ee87aSSven Van Asbroeck {
318308ee87aSSven Van Asbroeck 	if (t->done_fn)
319308ee87aSSven Van Asbroeck 		t->done_fn(cd);
320308ee87aSSven Van Asbroeck 	complete(&t->done);
321308ee87aSSven Van Asbroeck }
322308ee87aSSven Van Asbroeck 
323308ee87aSSven Van Asbroeck static void
ab_task_dequeue_finish_put(struct kfifo * q,struct anybuss_host * cd)324308ee87aSSven Van Asbroeck ab_task_dequeue_finish_put(struct kfifo *q, struct anybuss_host *cd)
325308ee87aSSven Van Asbroeck {
326308ee87aSSven Van Asbroeck 	int ret;
327308ee87aSSven Van Asbroeck 	struct ab_task *t;
328308ee87aSSven Van Asbroeck 
329308ee87aSSven Van Asbroeck 	ret = kfifo_out(q, &t, sizeof(t));
330308ee87aSSven Van Asbroeck 	WARN_ON(!ret);
331308ee87aSSven Van Asbroeck 	__ab_task_finish(t, cd);
332308ee87aSSven Van Asbroeck 	ab_task_put(t);
333308ee87aSSven Van Asbroeck }
334308ee87aSSven Van Asbroeck 
335308ee87aSSven Van Asbroeck static int
ab_task_enqueue(struct ab_task * t,struct kfifo * q,spinlock_t * slock,wait_queue_head_t * wq)336308ee87aSSven Van Asbroeck ab_task_enqueue(struct ab_task *t, struct kfifo *q, spinlock_t *slock,
337308ee87aSSven Van Asbroeck 		wait_queue_head_t *wq)
338308ee87aSSven Van Asbroeck {
339308ee87aSSven Van Asbroeck 	int ret;
340308ee87aSSven Van Asbroeck 
341308ee87aSSven Van Asbroeck 	t->start_jiffies = jiffies;
342308ee87aSSven Van Asbroeck 	__ab_task_get(t);
343308ee87aSSven Van Asbroeck 	ret = kfifo_in_spinlocked(q, &t, sizeof(t), slock);
344308ee87aSSven Van Asbroeck 	if (!ret) {
345308ee87aSSven Van Asbroeck 		ab_task_put(t);
346308ee87aSSven Van Asbroeck 		return -ENOMEM;
347308ee87aSSven Van Asbroeck 	}
348308ee87aSSven Van Asbroeck 	wake_up(wq);
349308ee87aSSven Van Asbroeck 	return 0;
350308ee87aSSven Van Asbroeck }
351308ee87aSSven Van Asbroeck 
352308ee87aSSven Van Asbroeck static int
ab_task_enqueue_wait(struct ab_task * t,struct kfifo * q,spinlock_t * slock,wait_queue_head_t * wq)353308ee87aSSven Van Asbroeck ab_task_enqueue_wait(struct ab_task *t, struct kfifo *q, spinlock_t *slock,
354308ee87aSSven Van Asbroeck 		     wait_queue_head_t *wq)
355308ee87aSSven Van Asbroeck {
356308ee87aSSven Van Asbroeck 	int ret;
357308ee87aSSven Van Asbroeck 
358308ee87aSSven Van Asbroeck 	ret = ab_task_enqueue(t, q, slock, wq);
359308ee87aSSven Van Asbroeck 	if (ret)
360308ee87aSSven Van Asbroeck 		return ret;
361308ee87aSSven Van Asbroeck 	ret = wait_for_completion_interruptible(&t->done);
362308ee87aSSven Van Asbroeck 	if (ret)
363308ee87aSSven Van Asbroeck 		return ret;
364308ee87aSSven Van Asbroeck 	return t->result;
365308ee87aSSven Van Asbroeck }
366308ee87aSSven Van Asbroeck 
367308ee87aSSven Van Asbroeck /* ------------------------ anybus hardware ------------------------ */
368308ee87aSSven Van Asbroeck 
369308ee87aSSven Van Asbroeck struct anybuss_host {
370308ee87aSSven Van Asbroeck 	struct device *dev;
371308ee87aSSven Van Asbroeck 	struct anybuss_client *client;
372308ee87aSSven Van Asbroeck 	void (*reset)(struct device *dev, bool assert);
373308ee87aSSven Van Asbroeck 	struct regmap *regmap;
374308ee87aSSven Van Asbroeck 	int irq;
375308ee87aSSven Van Asbroeck 	int host_idx;
376308ee87aSSven Van Asbroeck 	struct task_struct *qthread;
377308ee87aSSven Van Asbroeck 	wait_queue_head_t wq;
378308ee87aSSven Van Asbroeck 	struct completion card_boot;
379308ee87aSSven Van Asbroeck 	atomic_t ind_ab;
380308ee87aSSven Van Asbroeck 	spinlock_t qlock; /* protects IN side of powerq, mboxq, areaq */
381308ee87aSSven Van Asbroeck 	struct kmem_cache *qcache;
382308ee87aSSven Van Asbroeck 	struct kfifo qs[3];
383308ee87aSSven Van Asbroeck 	struct kfifo *powerq;
384308ee87aSSven Van Asbroeck 	struct kfifo *mboxq;
385308ee87aSSven Van Asbroeck 	struct kfifo *areaq;
386308ee87aSSven Van Asbroeck 	bool power_on;
387308ee87aSSven Van Asbroeck 	bool softint_pending;
388308ee87aSSven Van Asbroeck };
389308ee87aSSven Van Asbroeck 
reset_assert(struct anybuss_host * cd)390308ee87aSSven Van Asbroeck static void reset_assert(struct anybuss_host *cd)
391308ee87aSSven Van Asbroeck {
392308ee87aSSven Van Asbroeck 	cd->reset(cd->dev, true);
393308ee87aSSven Van Asbroeck }
394308ee87aSSven Van Asbroeck 
reset_deassert(struct anybuss_host * cd)395308ee87aSSven Van Asbroeck static void reset_deassert(struct anybuss_host *cd)
396308ee87aSSven Van Asbroeck {
397308ee87aSSven Van Asbroeck 	cd->reset(cd->dev, false);
398308ee87aSSven Van Asbroeck }
399308ee87aSSven Van Asbroeck 
test_dpram(struct regmap * regmap)400308ee87aSSven Van Asbroeck static int test_dpram(struct regmap *regmap)
401308ee87aSSven Van Asbroeck {
402308ee87aSSven Van Asbroeck 	int i;
403308ee87aSSven Van Asbroeck 	unsigned int val;
404308ee87aSSven Van Asbroeck 
405308ee87aSSven Van Asbroeck 	for (i = 0; i < DPRAM_SIZE; i++)
406308ee87aSSven Van Asbroeck 		regmap_write(regmap, i, (u8)i);
407308ee87aSSven Van Asbroeck 	for (i = 0; i < DPRAM_SIZE; i++) {
408308ee87aSSven Van Asbroeck 		regmap_read(regmap, i, &val);
409308ee87aSSven Van Asbroeck 		if ((u8)val != (u8)i)
410308ee87aSSven Van Asbroeck 			return -EIO;
411308ee87aSSven Van Asbroeck 	}
412308ee87aSSven Van Asbroeck 	return 0;
413308ee87aSSven Van Asbroeck }
414308ee87aSSven Van Asbroeck 
read_ind_ab(struct regmap * regmap)415308ee87aSSven Van Asbroeck static int read_ind_ab(struct regmap *regmap)
416308ee87aSSven Van Asbroeck {
417308ee87aSSven Van Asbroeck 	unsigned long timeout = jiffies + HZ / 2;
418308ee87aSSven Van Asbroeck 	unsigned int a, b, i = 0;
419308ee87aSSven Van Asbroeck 
420308ee87aSSven Van Asbroeck 	while (time_before_eq(jiffies, timeout)) {
421308ee87aSSven Van Asbroeck 		regmap_read(regmap, REG_IND_AB, &a);
422308ee87aSSven Van Asbroeck 		regmap_read(regmap, REG_IND_AB, &b);
423308ee87aSSven Van Asbroeck 		if (likely(a == b))
424308ee87aSSven Van Asbroeck 			return (int)a;
425308ee87aSSven Van Asbroeck 		if (i < 10) {
426308ee87aSSven Van Asbroeck 			cpu_relax();
427308ee87aSSven Van Asbroeck 			i++;
428308ee87aSSven Van Asbroeck 		} else {
429308ee87aSSven Van Asbroeck 			usleep_range(500, 1000);
430308ee87aSSven Van Asbroeck 		}
431308ee87aSSven Van Asbroeck 	}
432308ee87aSSven Van Asbroeck 	WARN(1, "IND_AB register not stable");
433308ee87aSSven Van Asbroeck 	return -ETIMEDOUT;
434308ee87aSSven Van Asbroeck }
435308ee87aSSven Van Asbroeck 
write_ind_ap(struct regmap * regmap,unsigned int ind_ap)436308ee87aSSven Van Asbroeck static int write_ind_ap(struct regmap *regmap, unsigned int ind_ap)
437308ee87aSSven Van Asbroeck {
438308ee87aSSven Van Asbroeck 	unsigned long timeout = jiffies + HZ / 2;
439308ee87aSSven Van Asbroeck 	unsigned int v, i = 0;
440308ee87aSSven Van Asbroeck 
441308ee87aSSven Van Asbroeck 	while (time_before_eq(jiffies, timeout)) {
442308ee87aSSven Van Asbroeck 		regmap_write(regmap, REG_IND_AP, ind_ap);
443308ee87aSSven Van Asbroeck 		regmap_read(regmap, REG_IND_AP, &v);
444308ee87aSSven Van Asbroeck 		if (likely(ind_ap == v))
445308ee87aSSven Van Asbroeck 			return 0;
446308ee87aSSven Van Asbroeck 		if (i < 10) {
447308ee87aSSven Van Asbroeck 			cpu_relax();
448308ee87aSSven Van Asbroeck 			i++;
449308ee87aSSven Van Asbroeck 		} else {
450308ee87aSSven Van Asbroeck 			usleep_range(500, 1000);
451308ee87aSSven Van Asbroeck 		}
452308ee87aSSven Van Asbroeck 	}
453308ee87aSSven Van Asbroeck 	WARN(1, "IND_AP register not stable");
454308ee87aSSven Van Asbroeck 	return -ETIMEDOUT;
455308ee87aSSven Van Asbroeck }
456308ee87aSSven Van Asbroeck 
irq_handler(int irq,void * data)457308ee87aSSven Van Asbroeck static irqreturn_t irq_handler(int irq, void *data)
458308ee87aSSven Van Asbroeck {
459308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = data;
460308ee87aSSven Van Asbroeck 	int ind_ab;
461308ee87aSSven Van Asbroeck 
462308ee87aSSven Van Asbroeck 	/*
463308ee87aSSven Van Asbroeck 	 * irq handler needs exclusive access to the IND_AB register,
464308ee87aSSven Van Asbroeck 	 * because the act of reading the register acks the interrupt.
465308ee87aSSven Van Asbroeck 	 *
466308ee87aSSven Van Asbroeck 	 * store the register value in cd->ind_ab (an atomic_t), so that the
467308ee87aSSven Van Asbroeck 	 * queue thread is able to read it without causing an interrupt ack
468308ee87aSSven Van Asbroeck 	 * side-effect (and without spuriously acking an interrupt).
469308ee87aSSven Van Asbroeck 	 */
470308ee87aSSven Van Asbroeck 	ind_ab = read_ind_ab(cd->regmap);
471308ee87aSSven Van Asbroeck 	if (ind_ab < 0)
472308ee87aSSven Van Asbroeck 		return IRQ_NONE;
473308ee87aSSven Van Asbroeck 	atomic_set(&cd->ind_ab, ind_ab);
474308ee87aSSven Van Asbroeck 	complete(&cd->card_boot);
475308ee87aSSven Van Asbroeck 	wake_up(&cd->wq);
476308ee87aSSven Van Asbroeck 	return IRQ_HANDLED;
477308ee87aSSven Van Asbroeck }
478308ee87aSSven Van Asbroeck 
479308ee87aSSven Van Asbroeck /* ------------------------ power on/off tasks --------------------- */
480308ee87aSSven Van Asbroeck 
task_fn_power_off(struct anybuss_host * cd,struct ab_task * t)481308ee87aSSven Van Asbroeck static int task_fn_power_off(struct anybuss_host *cd,
482308ee87aSSven Van Asbroeck 			     struct ab_task *t)
483308ee87aSSven Van Asbroeck {
484308ee87aSSven Van Asbroeck 	struct anybuss_client *client = cd->client;
485308ee87aSSven Van Asbroeck 
486308ee87aSSven Van Asbroeck 	if (!cd->power_on)
487308ee87aSSven Van Asbroeck 		return 0;
488308ee87aSSven Van Asbroeck 	disable_irq(cd->irq);
489308ee87aSSven Van Asbroeck 	reset_assert(cd);
490308ee87aSSven Van Asbroeck 	atomic_set(&cd->ind_ab, IND_AB_UPDATED);
491308ee87aSSven Van Asbroeck 	if (client->on_online_changed)
492308ee87aSSven Van Asbroeck 		client->on_online_changed(client, false);
493308ee87aSSven Van Asbroeck 	cd->power_on = false;
494308ee87aSSven Van Asbroeck 	return 0;
495308ee87aSSven Van Asbroeck }
496308ee87aSSven Van Asbroeck 
task_fn_power_on_2(struct anybuss_host * cd,struct ab_task * t)497308ee87aSSven Van Asbroeck static int task_fn_power_on_2(struct anybuss_host *cd,
498308ee87aSSven Van Asbroeck 			      struct ab_task *t)
499308ee87aSSven Van Asbroeck {
500308ee87aSSven Van Asbroeck 	if (completion_done(&cd->card_boot)) {
501308ee87aSSven Van Asbroeck 		cd->power_on = true;
502308ee87aSSven Van Asbroeck 		return 0;
503308ee87aSSven Van Asbroeck 	}
504308ee87aSSven Van Asbroeck 	if (time_after(jiffies, t->start_jiffies + TIMEOUT)) {
505308ee87aSSven Van Asbroeck 		disable_irq(cd->irq);
506308ee87aSSven Van Asbroeck 		reset_assert(cd);
507308ee87aSSven Van Asbroeck 		dev_err(cd->dev, "power on timed out");
508308ee87aSSven Van Asbroeck 		return -ETIMEDOUT;
509308ee87aSSven Van Asbroeck 	}
510308ee87aSSven Van Asbroeck 	return -EINPROGRESS;
511308ee87aSSven Van Asbroeck }
512308ee87aSSven Van Asbroeck 
task_fn_power_on(struct anybuss_host * cd,struct ab_task * t)513308ee87aSSven Van Asbroeck static int task_fn_power_on(struct anybuss_host *cd,
514308ee87aSSven Van Asbroeck 			    struct ab_task *t)
515308ee87aSSven Van Asbroeck {
516308ee87aSSven Van Asbroeck 	unsigned int dummy;
517308ee87aSSven Van Asbroeck 
518308ee87aSSven Van Asbroeck 	if (cd->power_on)
519308ee87aSSven Van Asbroeck 		return 0;
520308ee87aSSven Van Asbroeck 	/*
521308ee87aSSven Van Asbroeck 	 * anybus docs: prevent false 'init done' interrupt by
522308ee87aSSven Van Asbroeck 	 * doing a dummy read of IND_AB register while in reset.
523308ee87aSSven Van Asbroeck 	 */
524308ee87aSSven Van Asbroeck 	regmap_read(cd->regmap, REG_IND_AB, &dummy);
525308ee87aSSven Van Asbroeck 	reinit_completion(&cd->card_boot);
526308ee87aSSven Van Asbroeck 	enable_irq(cd->irq);
527308ee87aSSven Van Asbroeck 	reset_deassert(cd);
528308ee87aSSven Van Asbroeck 	t->task_fn = task_fn_power_on_2;
529308ee87aSSven Van Asbroeck 	return -EINPROGRESS;
530308ee87aSSven Van Asbroeck }
531308ee87aSSven Van Asbroeck 
anybuss_set_power(struct anybuss_client * client,bool power_on)532308ee87aSSven Van Asbroeck int anybuss_set_power(struct anybuss_client *client, bool power_on)
533308ee87aSSven Van Asbroeck {
534308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = client->host;
535308ee87aSSven Van Asbroeck 	struct ab_task *t;
536308ee87aSSven Van Asbroeck 	int err;
537308ee87aSSven Van Asbroeck 
538308ee87aSSven Van Asbroeck 	t = ab_task_create_get(cd->qcache, power_on ?
539308ee87aSSven Van Asbroeck 				task_fn_power_on : task_fn_power_off);
540308ee87aSSven Van Asbroeck 	if (!t)
541308ee87aSSven Van Asbroeck 		return -ENOMEM;
542308ee87aSSven Van Asbroeck 	err = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
543308ee87aSSven Van Asbroeck 	ab_task_put(t);
544308ee87aSSven Van Asbroeck 	return err;
545308ee87aSSven Van Asbroeck }
546308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_set_power);
547308ee87aSSven Van Asbroeck 
548308ee87aSSven Van Asbroeck /* ---------------------------- area tasks ------------------------ */
549308ee87aSSven Van Asbroeck 
task_fn_area_3(struct anybuss_host * cd,struct ab_task * t)550308ee87aSSven Van Asbroeck static int task_fn_area_3(struct anybuss_host *cd, struct ab_task *t)
551308ee87aSSven Van Asbroeck {
552308ee87aSSven Van Asbroeck 	struct area_priv *pd = &t->area_pd;
553308ee87aSSven Van Asbroeck 
554308ee87aSSven Van Asbroeck 	if (!cd->power_on)
555308ee87aSSven Van Asbroeck 		return -EIO;
556308ee87aSSven Van Asbroeck 	if (atomic_read(&cd->ind_ab) & pd->flags) {
557308ee87aSSven Van Asbroeck 		/* area not released yet */
558308ee87aSSven Van Asbroeck 		if (time_after(jiffies, t->start_jiffies + TIMEOUT))
559308ee87aSSven Van Asbroeck 			return -ETIMEDOUT;
560308ee87aSSven Van Asbroeck 		return -EINPROGRESS;
561308ee87aSSven Van Asbroeck 	}
562308ee87aSSven Van Asbroeck 	return 0;
563308ee87aSSven Van Asbroeck }
564308ee87aSSven Van Asbroeck 
task_fn_area_2(struct anybuss_host * cd,struct ab_task * t)565308ee87aSSven Van Asbroeck static int task_fn_area_2(struct anybuss_host *cd, struct ab_task *t)
566308ee87aSSven Van Asbroeck {
567308ee87aSSven Van Asbroeck 	struct area_priv *pd = &t->area_pd;
568308ee87aSSven Van Asbroeck 	unsigned int ind_ap;
569308ee87aSSven Van Asbroeck 	int ret;
570308ee87aSSven Van Asbroeck 
571308ee87aSSven Van Asbroeck 	if (!cd->power_on)
572308ee87aSSven Van Asbroeck 		return -EIO;
573308ee87aSSven Van Asbroeck 	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
574308ee87aSSven Van Asbroeck 	if (!(atomic_read(&cd->ind_ab) & pd->flags)) {
575308ee87aSSven Van Asbroeck 		/* we don't own the area yet */
576308ee87aSSven Van Asbroeck 		if (time_after(jiffies, t->start_jiffies + TIMEOUT)) {
577308ee87aSSven Van Asbroeck 			dev_warn(cd->dev, "timeout waiting for area");
578308ee87aSSven Van Asbroeck 			dump_stack();
579308ee87aSSven Van Asbroeck 			return -ETIMEDOUT;
580308ee87aSSven Van Asbroeck 		}
581308ee87aSSven Van Asbroeck 		return -EINPROGRESS;
582308ee87aSSven Van Asbroeck 	}
583308ee87aSSven Van Asbroeck 	/* we own the area, do what we're here to do */
584308ee87aSSven Van Asbroeck 	if (pd->is_write)
585308ee87aSSven Van Asbroeck 		regmap_bulk_write(cd->regmap, pd->addr, pd->buf,
586308ee87aSSven Van Asbroeck 				  pd->count);
587308ee87aSSven Van Asbroeck 	else
588308ee87aSSven Van Asbroeck 		regmap_bulk_read(cd->regmap, pd->addr, pd->buf,
589308ee87aSSven Van Asbroeck 				 pd->count);
590308ee87aSSven Van Asbroeck 	/* ask to release the area, must use unlocked release */
591308ee87aSSven Van Asbroeck 	ind_ap &= ~IND_AP_ABITS;
592308ee87aSSven Van Asbroeck 	ind_ap |= pd->flags;
593308ee87aSSven Van Asbroeck 	ret = write_ind_ap(cd->regmap, ind_ap);
594308ee87aSSven Van Asbroeck 	if (ret)
595308ee87aSSven Van Asbroeck 		return ret;
596308ee87aSSven Van Asbroeck 	t->task_fn = task_fn_area_3;
597308ee87aSSven Van Asbroeck 	return -EINPROGRESS;
598308ee87aSSven Van Asbroeck }
599308ee87aSSven Van Asbroeck 
task_fn_area(struct anybuss_host * cd,struct ab_task * t)600308ee87aSSven Van Asbroeck static int task_fn_area(struct anybuss_host *cd, struct ab_task *t)
601308ee87aSSven Van Asbroeck {
602308ee87aSSven Van Asbroeck 	struct area_priv *pd = &t->area_pd;
603308ee87aSSven Van Asbroeck 	unsigned int ind_ap;
604308ee87aSSven Van Asbroeck 	int ret;
605308ee87aSSven Van Asbroeck 
606308ee87aSSven Van Asbroeck 	if (!cd->power_on)
607308ee87aSSven Van Asbroeck 		return -EIO;
608308ee87aSSven Van Asbroeck 	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
609308ee87aSSven Van Asbroeck 	/* ask to take the area */
610308ee87aSSven Van Asbroeck 	ind_ap &= ~IND_AP_ABITS;
611308ee87aSSven Van Asbroeck 	ind_ap |= pd->flags | IND_AP_ACTION | IND_AP_LOCK;
612308ee87aSSven Van Asbroeck 	ret = write_ind_ap(cd->regmap, ind_ap);
613308ee87aSSven Van Asbroeck 	if (ret)
614308ee87aSSven Van Asbroeck 		return ret;
615308ee87aSSven Van Asbroeck 	t->task_fn = task_fn_area_2;
616308ee87aSSven Van Asbroeck 	return -EINPROGRESS;
617308ee87aSSven Van Asbroeck }
618308ee87aSSven Van Asbroeck 
619308ee87aSSven Van Asbroeck static struct ab_task *
create_area_reader(struct kmem_cache * qcache,u16 flags,u16 addr,size_t count)620308ee87aSSven Van Asbroeck create_area_reader(struct kmem_cache *qcache, u16 flags, u16 addr,
621308ee87aSSven Van Asbroeck 		   size_t count)
622308ee87aSSven Van Asbroeck {
623308ee87aSSven Van Asbroeck 	struct ab_task *t;
624308ee87aSSven Van Asbroeck 	struct area_priv *ap;
625308ee87aSSven Van Asbroeck 
626308ee87aSSven Van Asbroeck 	t = ab_task_create_get(qcache, task_fn_area);
627308ee87aSSven Van Asbroeck 	if (!t)
628308ee87aSSven Van Asbroeck 		return NULL;
629308ee87aSSven Van Asbroeck 	ap = &t->area_pd;
630308ee87aSSven Van Asbroeck 	ap->flags = flags;
631308ee87aSSven Van Asbroeck 	ap->addr = addr;
632308ee87aSSven Van Asbroeck 	ap->is_write = false;
633308ee87aSSven Van Asbroeck 	ap->count = count;
634308ee87aSSven Van Asbroeck 	return t;
635308ee87aSSven Van Asbroeck }
636308ee87aSSven Van Asbroeck 
637308ee87aSSven Van Asbroeck static struct ab_task *
create_area_writer(struct kmem_cache * qcache,u16 flags,u16 addr,const void * buf,size_t count)638308ee87aSSven Van Asbroeck create_area_writer(struct kmem_cache *qcache, u16 flags, u16 addr,
639308ee87aSSven Van Asbroeck 		   const void *buf, size_t count)
640308ee87aSSven Van Asbroeck {
641308ee87aSSven Van Asbroeck 	struct ab_task *t;
642308ee87aSSven Van Asbroeck 	struct area_priv *ap;
643308ee87aSSven Van Asbroeck 
644308ee87aSSven Van Asbroeck 	t = ab_task_create_get(qcache, task_fn_area);
645308ee87aSSven Van Asbroeck 	if (!t)
646308ee87aSSven Van Asbroeck 		return NULL;
647308ee87aSSven Van Asbroeck 	ap = &t->area_pd;
648308ee87aSSven Van Asbroeck 	ap->flags = flags;
649308ee87aSSven Van Asbroeck 	ap->addr = addr;
650308ee87aSSven Van Asbroeck 	ap->is_write = true;
651308ee87aSSven Van Asbroeck 	ap->count = count;
652308ee87aSSven Van Asbroeck 	memcpy(ap->buf, buf, count);
653308ee87aSSven Van Asbroeck 	return t;
654308ee87aSSven Van Asbroeck }
655308ee87aSSven Van Asbroeck 
656308ee87aSSven Van Asbroeck static struct ab_task *
create_area_user_writer(struct kmem_cache * qcache,u16 flags,u16 addr,const void __user * buf,size_t count)657308ee87aSSven Van Asbroeck create_area_user_writer(struct kmem_cache *qcache, u16 flags, u16 addr,
658308ee87aSSven Van Asbroeck 			const void __user *buf, size_t count)
659308ee87aSSven Van Asbroeck {
660308ee87aSSven Van Asbroeck 	struct ab_task *t;
661308ee87aSSven Van Asbroeck 	struct area_priv *ap;
662308ee87aSSven Van Asbroeck 
663308ee87aSSven Van Asbroeck 	t = ab_task_create_get(qcache, task_fn_area);
664308ee87aSSven Van Asbroeck 	if (!t)
665308ee87aSSven Van Asbroeck 		return ERR_PTR(-ENOMEM);
666308ee87aSSven Van Asbroeck 	ap = &t->area_pd;
667308ee87aSSven Van Asbroeck 	ap->flags = flags;
668308ee87aSSven Van Asbroeck 	ap->addr = addr;
669308ee87aSSven Van Asbroeck 	ap->is_write = true;
670308ee87aSSven Van Asbroeck 	ap->count = count;
671308ee87aSSven Van Asbroeck 	if (copy_from_user(ap->buf, buf, count)) {
672308ee87aSSven Van Asbroeck 		ab_task_put(t);
673308ee87aSSven Van Asbroeck 		return ERR_PTR(-EFAULT);
674308ee87aSSven Van Asbroeck 	}
675308ee87aSSven Van Asbroeck 	return t;
676308ee87aSSven Van Asbroeck }
677308ee87aSSven Van Asbroeck 
area_range_ok(u16 addr,size_t count,u16 area_start,size_t area_sz)678308ee87aSSven Van Asbroeck static bool area_range_ok(u16 addr, size_t count, u16 area_start,
679308ee87aSSven Van Asbroeck 			  size_t area_sz)
680308ee87aSSven Van Asbroeck {
681308ee87aSSven Van Asbroeck 	u16 area_end_ex = area_start + area_sz;
682308ee87aSSven Van Asbroeck 	u16 addr_end_ex;
683308ee87aSSven Van Asbroeck 
684308ee87aSSven Van Asbroeck 	if (addr < area_start)
685308ee87aSSven Van Asbroeck 		return false;
686308ee87aSSven Van Asbroeck 	if (addr >= area_end_ex)
687308ee87aSSven Van Asbroeck 		return false;
688308ee87aSSven Van Asbroeck 	addr_end_ex = addr + count;
689308ee87aSSven Van Asbroeck 	if (addr_end_ex > area_end_ex)
690308ee87aSSven Van Asbroeck 		return false;
691308ee87aSSven Van Asbroeck 	return true;
692308ee87aSSven Van Asbroeck }
693308ee87aSSven Van Asbroeck 
694308ee87aSSven Van Asbroeck /* -------------------------- mailbox tasks ----------------------- */
695308ee87aSSven Van Asbroeck 
task_fn_mbox_2(struct anybuss_host * cd,struct ab_task * t)696308ee87aSSven Van Asbroeck static int task_fn_mbox_2(struct anybuss_host *cd, struct ab_task *t)
697308ee87aSSven Van Asbroeck {
698308ee87aSSven Van Asbroeck 	struct mbox_priv *pd = &t->mbox_pd;
699308ee87aSSven Van Asbroeck 	unsigned int ind_ap;
700308ee87aSSven Van Asbroeck 
701308ee87aSSven Van Asbroeck 	if (!cd->power_on)
702308ee87aSSven Van Asbroeck 		return -EIO;
703308ee87aSSven Van Asbroeck 	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
704308ee87aSSven Van Asbroeck 	if (((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_MOUT) == 0) {
705308ee87aSSven Van Asbroeck 		/* output message not here */
706308ee87aSSven Van Asbroeck 		if (time_after(jiffies, t->start_jiffies + TIMEOUT))
707308ee87aSSven Van Asbroeck 			return -ETIMEDOUT;
708308ee87aSSven Van Asbroeck 		return -EINPROGRESS;
709308ee87aSSven Van Asbroeck 	}
710308ee87aSSven Van Asbroeck 	/* grab the returned header and msg */
711308ee87aSSven Van Asbroeck 	regmap_bulk_read(cd->regmap, MBOX_OUT_AREA, &pd->hdr,
712308ee87aSSven Van Asbroeck 			 sizeof(pd->hdr));
713308ee87aSSven Van Asbroeck 	regmap_bulk_read(cd->regmap, MBOX_OUT_AREA + sizeof(pd->hdr),
714308ee87aSSven Van Asbroeck 			 pd->msg, pd->msg_in_sz);
715308ee87aSSven Van Asbroeck 	/* tell anybus we've consumed the message */
716308ee87aSSven Van Asbroeck 	ind_ap ^= IND_AX_MOUT;
717308ee87aSSven Van Asbroeck 	return write_ind_ap(cd->regmap, ind_ap);
718308ee87aSSven Van Asbroeck }
719308ee87aSSven Van Asbroeck 
task_fn_mbox(struct anybuss_host * cd,struct ab_task * t)720308ee87aSSven Van Asbroeck static int task_fn_mbox(struct anybuss_host *cd, struct ab_task *t)
721308ee87aSSven Van Asbroeck {
722308ee87aSSven Van Asbroeck 	struct mbox_priv *pd = &t->mbox_pd;
723308ee87aSSven Van Asbroeck 	unsigned int ind_ap;
724308ee87aSSven Van Asbroeck 	int ret;
725308ee87aSSven Van Asbroeck 
726308ee87aSSven Van Asbroeck 	if (!cd->power_on)
727308ee87aSSven Van Asbroeck 		return -EIO;
728308ee87aSSven Van Asbroeck 	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
729308ee87aSSven Van Asbroeck 	if ((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_MIN) {
730308ee87aSSven Van Asbroeck 		/* mbox input area busy */
731308ee87aSSven Van Asbroeck 		if (time_after(jiffies, t->start_jiffies + TIMEOUT))
732308ee87aSSven Van Asbroeck 			return -ETIMEDOUT;
733308ee87aSSven Van Asbroeck 		return -EINPROGRESS;
734308ee87aSSven Van Asbroeck 	}
735308ee87aSSven Van Asbroeck 	/* write the header and msg to input area */
736308ee87aSSven Van Asbroeck 	regmap_bulk_write(cd->regmap, MBOX_IN_AREA, &pd->hdr,
737308ee87aSSven Van Asbroeck 			  sizeof(pd->hdr));
738308ee87aSSven Van Asbroeck 	regmap_bulk_write(cd->regmap, MBOX_IN_AREA + sizeof(pd->hdr),
739308ee87aSSven Van Asbroeck 			  pd->msg, pd->msg_out_sz);
740308ee87aSSven Van Asbroeck 	/* tell anybus we gave it a message */
741308ee87aSSven Van Asbroeck 	ind_ap ^= IND_AX_MIN;
742308ee87aSSven Van Asbroeck 	ret = write_ind_ap(cd->regmap, ind_ap);
743308ee87aSSven Van Asbroeck 	if (ret)
744308ee87aSSven Van Asbroeck 		return ret;
745308ee87aSSven Van Asbroeck 	t->start_jiffies = jiffies;
746308ee87aSSven Van Asbroeck 	t->task_fn = task_fn_mbox_2;
747308ee87aSSven Van Asbroeck 	return -EINPROGRESS;
748308ee87aSSven Van Asbroeck }
749308ee87aSSven Van Asbroeck 
log_invalid_other(struct device * dev,struct anybus_mbox_hdr * hdr)750308ee87aSSven Van Asbroeck static void log_invalid_other(struct device *dev,
751308ee87aSSven Van Asbroeck 			      struct anybus_mbox_hdr *hdr)
752308ee87aSSven Van Asbroeck {
753308ee87aSSven Van Asbroeck 	size_t ext_offs = ARRAY_SIZE(hdr->extended) - 1;
754308ee87aSSven Van Asbroeck 	u16 code = be16_to_cpu(hdr->extended[ext_offs]);
755308ee87aSSven Van Asbroeck 
756308ee87aSSven Van Asbroeck 	dev_err(dev, "   Invalid other: [0x%02X]", code);
757308ee87aSSven Van Asbroeck }
758308ee87aSSven Van Asbroeck 
759308ee87aSSven Van Asbroeck static const char * const EMSGS[] = {
760308ee87aSSven Van Asbroeck 	"Invalid Message ID",
761308ee87aSSven Van Asbroeck 	"Invalid Message Type",
762308ee87aSSven Van Asbroeck 	"Invalid Command",
763308ee87aSSven Van Asbroeck 	"Invalid Data Size",
764308ee87aSSven Van Asbroeck 	"Message Header Malformed (offset 008h)",
765308ee87aSSven Van Asbroeck 	"Message Header Malformed (offset 00Ah)",
766308ee87aSSven Van Asbroeck 	"Message Header Malformed (offset 00Ch - 00Dh)",
767308ee87aSSven Van Asbroeck 	"Invalid Address",
768308ee87aSSven Van Asbroeck 	"Invalid Response",
769308ee87aSSven Van Asbroeck 	"Flash Config Error",
770308ee87aSSven Van Asbroeck };
771308ee87aSSven Van Asbroeck 
mbox_cmd_err(struct device * dev,struct mbox_priv * mpriv)772308ee87aSSven Van Asbroeck static int mbox_cmd_err(struct device *dev, struct mbox_priv *mpriv)
773308ee87aSSven Van Asbroeck {
774308ee87aSSven Van Asbroeck 	int i;
775308ee87aSSven Van Asbroeck 	u8 ecode;
776308ee87aSSven Van Asbroeck 	struct anybus_mbox_hdr *hdr = &mpriv->hdr;
777308ee87aSSven Van Asbroeck 	u16 info = be16_to_cpu(hdr->info);
778308ee87aSSven Van Asbroeck 	u8 *phdr = (u8 *)hdr;
779308ee87aSSven Van Asbroeck 	u8 *pmsg = mpriv->msg;
780308ee87aSSven Van Asbroeck 
781308ee87aSSven Van Asbroeck 	if (!(info & 0x8000))
782308ee87aSSven Van Asbroeck 		return 0;
783308ee87aSSven Van Asbroeck 	ecode = (info >> 8) & 0x0F;
784308ee87aSSven Van Asbroeck 	dev_err(dev, "mailbox command failed:");
785308ee87aSSven Van Asbroeck 	if (ecode == 0x0F)
786308ee87aSSven Van Asbroeck 		log_invalid_other(dev, hdr);
787308ee87aSSven Van Asbroeck 	else if (ecode < ARRAY_SIZE(EMSGS))
788308ee87aSSven Van Asbroeck 		dev_err(dev, "   Error code: %s (0x%02X)",
789308ee87aSSven Van Asbroeck 			EMSGS[ecode], ecode);
790308ee87aSSven Van Asbroeck 	else
791308ee87aSSven Van Asbroeck 		dev_err(dev, "   Error code: 0x%02X\n", ecode);
792308ee87aSSven Van Asbroeck 	dev_err(dev, "Failed command:");
793308ee87aSSven Van Asbroeck 	dev_err(dev, "Message Header:");
794308ee87aSSven Van Asbroeck 	for (i = 0; i < sizeof(mpriv->hdr); i += 2)
795308ee87aSSven Van Asbroeck 		dev_err(dev, "%02X%02X", phdr[i], phdr[i + 1]);
796308ee87aSSven Van Asbroeck 	dev_err(dev, "Message Data:");
797308ee87aSSven Van Asbroeck 	for (i = 0; i < mpriv->msg_in_sz; i += 2)
798308ee87aSSven Van Asbroeck 		dev_err(dev, "%02X%02X", pmsg[i], pmsg[i + 1]);
799308ee87aSSven Van Asbroeck 	dev_err(dev, "Stack dump:");
800308ee87aSSven Van Asbroeck 	dump_stack();
801308ee87aSSven Van Asbroeck 	return -EIO;
802308ee87aSSven Van Asbroeck }
803308ee87aSSven Van Asbroeck 
_anybus_mbox_cmd(struct anybuss_host * cd,u16 cmd_num,bool is_fb_cmd,const void * msg_out,size_t msg_out_sz,void * msg_in,size_t msg_in_sz,const void * ext,size_t ext_sz)804308ee87aSSven Van Asbroeck static int _anybus_mbox_cmd(struct anybuss_host *cd,
805308ee87aSSven Van Asbroeck 			    u16 cmd_num, bool is_fb_cmd,
806308ee87aSSven Van Asbroeck 				const void *msg_out, size_t msg_out_sz,
807308ee87aSSven Van Asbroeck 				void *msg_in, size_t msg_in_sz,
808308ee87aSSven Van Asbroeck 				const void *ext, size_t ext_sz)
809308ee87aSSven Van Asbroeck {
810308ee87aSSven Van Asbroeck 	struct ab_task *t;
811308ee87aSSven Van Asbroeck 	struct mbox_priv *pd;
812308ee87aSSven Van Asbroeck 	struct anybus_mbox_hdr *h;
813308ee87aSSven Van Asbroeck 	size_t msg_sz = max(msg_in_sz, msg_out_sz);
814308ee87aSSven Van Asbroeck 	u16 info;
815308ee87aSSven Van Asbroeck 	int err;
816308ee87aSSven Van Asbroeck 
817308ee87aSSven Van Asbroeck 	if (msg_sz > MAX_MBOX_MSG_SZ)
818308ee87aSSven Van Asbroeck 		return -EINVAL;
819308ee87aSSven Van Asbroeck 	if (ext && ext_sz > sizeof(h->extended))
820308ee87aSSven Van Asbroeck 		return -EINVAL;
821308ee87aSSven Van Asbroeck 	t = ab_task_create_get(cd->qcache, task_fn_mbox);
822308ee87aSSven Van Asbroeck 	if (!t)
823308ee87aSSven Van Asbroeck 		return -ENOMEM;
824308ee87aSSven Van Asbroeck 	pd = &t->mbox_pd;
825308ee87aSSven Van Asbroeck 	h = &pd->hdr;
826308ee87aSSven Van Asbroeck 	info = is_fb_cmd ? INFO_TYPE_FB : INFO_TYPE_APP;
827308ee87aSSven Van Asbroeck 	/*
828308ee87aSSven Van Asbroeck 	 * prevent uninitialized memory in the header from being sent
829308ee87aSSven Van Asbroeck 	 * across the anybus
830308ee87aSSven Van Asbroeck 	 */
831308ee87aSSven Van Asbroeck 	memset(h, 0, sizeof(*h));
832308ee87aSSven Van Asbroeck 	h->info = cpu_to_be16(info | INFO_COMMAND);
833308ee87aSSven Van Asbroeck 	h->cmd_num = cpu_to_be16(cmd_num);
834308ee87aSSven Van Asbroeck 	h->data_size = cpu_to_be16(msg_out_sz);
835308ee87aSSven Van Asbroeck 	h->frame_count = cpu_to_be16(1);
836308ee87aSSven Van Asbroeck 	h->frame_num = cpu_to_be16(1);
837308ee87aSSven Van Asbroeck 	h->offset_high = cpu_to_be16(0);
838308ee87aSSven Van Asbroeck 	h->offset_low = cpu_to_be16(0);
839308ee87aSSven Van Asbroeck 	if (ext)
840308ee87aSSven Van Asbroeck 		memcpy(h->extended, ext, ext_sz);
841308ee87aSSven Van Asbroeck 	memcpy(pd->msg, msg_out, msg_out_sz);
842308ee87aSSven Van Asbroeck 	pd->msg_out_sz = msg_out_sz;
843308ee87aSSven Van Asbroeck 	pd->msg_in_sz = msg_in_sz;
844308ee87aSSven Van Asbroeck 	err = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
845308ee87aSSven Van Asbroeck 	if (err)
846308ee87aSSven Van Asbroeck 		goto out;
847308ee87aSSven Van Asbroeck 	/*
848308ee87aSSven Van Asbroeck 	 * mailbox mechanism worked ok, but maybe the mbox response
849308ee87aSSven Van Asbroeck 	 * contains an error ?
850308ee87aSSven Van Asbroeck 	 */
851308ee87aSSven Van Asbroeck 	err = mbox_cmd_err(cd->dev, pd);
852308ee87aSSven Van Asbroeck 	if (err)
853308ee87aSSven Van Asbroeck 		goto out;
854308ee87aSSven Van Asbroeck 	memcpy(msg_in, pd->msg, msg_in_sz);
855308ee87aSSven Van Asbroeck out:
856308ee87aSSven Van Asbroeck 	ab_task_put(t);
857308ee87aSSven Van Asbroeck 	return err;
858308ee87aSSven Van Asbroeck }
859308ee87aSSven Van Asbroeck 
860308ee87aSSven Van Asbroeck /* ------------------------ anybus queues ------------------------ */
861308ee87aSSven Van Asbroeck 
process_q(struct anybuss_host * cd,struct kfifo * q)862308ee87aSSven Van Asbroeck static void process_q(struct anybuss_host *cd, struct kfifo *q)
863308ee87aSSven Van Asbroeck {
864308ee87aSSven Van Asbroeck 	struct ab_task *t;
865308ee87aSSven Van Asbroeck 	int ret;
866308ee87aSSven Van Asbroeck 
867308ee87aSSven Van Asbroeck 	ret = kfifo_out_peek(q, &t, sizeof(t));
868308ee87aSSven Van Asbroeck 	if (!ret)
869308ee87aSSven Van Asbroeck 		return;
870308ee87aSSven Van Asbroeck 	t->result = t->task_fn(cd, t);
871308ee87aSSven Van Asbroeck 	if (t->result != -EINPROGRESS)
872308ee87aSSven Van Asbroeck 		ab_task_dequeue_finish_put(q, cd);
873308ee87aSSven Van Asbroeck }
874308ee87aSSven Van Asbroeck 
qs_have_work(struct kfifo * qs,size_t num)875308ee87aSSven Van Asbroeck static bool qs_have_work(struct kfifo *qs, size_t num)
876308ee87aSSven Van Asbroeck {
877308ee87aSSven Van Asbroeck 	size_t i;
878308ee87aSSven Van Asbroeck 	struct ab_task *t;
879308ee87aSSven Van Asbroeck 	int ret;
880308ee87aSSven Van Asbroeck 
881308ee87aSSven Van Asbroeck 	for (i = 0; i < num; i++, qs++) {
882308ee87aSSven Van Asbroeck 		ret = kfifo_out_peek(qs, &t, sizeof(t));
883308ee87aSSven Van Asbroeck 		if (ret && (t->result != -EINPROGRESS))
884308ee87aSSven Van Asbroeck 			return true;
885308ee87aSSven Van Asbroeck 	}
886308ee87aSSven Van Asbroeck 	return false;
887308ee87aSSven Van Asbroeck }
888308ee87aSSven Van Asbroeck 
process_qs(struct anybuss_host * cd)889308ee87aSSven Van Asbroeck static void process_qs(struct anybuss_host *cd)
890308ee87aSSven Van Asbroeck {
891308ee87aSSven Van Asbroeck 	size_t i;
892308ee87aSSven Van Asbroeck 	struct kfifo *qs = cd->qs;
893308ee87aSSven Van Asbroeck 	size_t nqs = ARRAY_SIZE(cd->qs);
894308ee87aSSven Van Asbroeck 
895308ee87aSSven Van Asbroeck 	for (i = 0; i < nqs; i++, qs++)
896308ee87aSSven Van Asbroeck 		process_q(cd, qs);
897308ee87aSSven Van Asbroeck }
898308ee87aSSven Van Asbroeck 
softint_ack(struct anybuss_host * cd)899308ee87aSSven Van Asbroeck static void softint_ack(struct anybuss_host *cd)
900308ee87aSSven Van Asbroeck {
901308ee87aSSven Van Asbroeck 	unsigned int ind_ap;
902308ee87aSSven Van Asbroeck 
903308ee87aSSven Van Asbroeck 	cd->softint_pending = false;
904308ee87aSSven Van Asbroeck 	if (!cd->power_on)
905308ee87aSSven Van Asbroeck 		return;
906308ee87aSSven Van Asbroeck 	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
907308ee87aSSven Van Asbroeck 	ind_ap &= ~IND_AX_EVNT;
908308ee87aSSven Van Asbroeck 	ind_ap |= atomic_read(&cd->ind_ab) & IND_AX_EVNT;
909308ee87aSSven Van Asbroeck 	write_ind_ap(cd->regmap, ind_ap);
910308ee87aSSven Van Asbroeck }
911308ee87aSSven Van Asbroeck 
process_softint(struct anybuss_host * cd)912308ee87aSSven Van Asbroeck static void process_softint(struct anybuss_host *cd)
913308ee87aSSven Van Asbroeck {
914308ee87aSSven Van Asbroeck 	struct anybuss_client *client = cd->client;
915308ee87aSSven Van Asbroeck 	static const u8 zero;
916308ee87aSSven Van Asbroeck 	int ret;
917308ee87aSSven Van Asbroeck 	unsigned int ind_ap, ev;
918308ee87aSSven Van Asbroeck 	struct ab_task *t;
919308ee87aSSven Van Asbroeck 
920308ee87aSSven Van Asbroeck 	if (!cd->power_on)
921308ee87aSSven Van Asbroeck 		return;
922308ee87aSSven Van Asbroeck 	if (cd->softint_pending)
923308ee87aSSven Van Asbroeck 		return;
924308ee87aSSven Van Asbroeck 	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
925308ee87aSSven Van Asbroeck 	if (!((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_EVNT))
926308ee87aSSven Van Asbroeck 		return;
927308ee87aSSven Van Asbroeck 	/* process software interrupt */
928308ee87aSSven Van Asbroeck 	regmap_read(cd->regmap, REG_EVENT_CAUSE, &ev);
929308ee87aSSven Van Asbroeck 	if (ev & EVENT_CAUSE_FBON) {
930308ee87aSSven Van Asbroeck 		if (client->on_online_changed)
931308ee87aSSven Van Asbroeck 			client->on_online_changed(client, true);
932308ee87aSSven Van Asbroeck 		dev_dbg(cd->dev, "Fieldbus ON");
933308ee87aSSven Van Asbroeck 	}
934308ee87aSSven Van Asbroeck 	if (ev & EVENT_CAUSE_FBOF) {
935308ee87aSSven Van Asbroeck 		if (client->on_online_changed)
936308ee87aSSven Van Asbroeck 			client->on_online_changed(client, false);
937308ee87aSSven Van Asbroeck 		dev_dbg(cd->dev, "Fieldbus OFF");
938308ee87aSSven Van Asbroeck 	}
939308ee87aSSven Van Asbroeck 	if (ev & EVENT_CAUSE_DC) {
940308ee87aSSven Van Asbroeck 		if (client->on_area_updated)
941308ee87aSSven Van Asbroeck 			client->on_area_updated(client);
942308ee87aSSven Van Asbroeck 		dev_dbg(cd->dev, "Fieldbus data changed");
943308ee87aSSven Van Asbroeck 	}
944308ee87aSSven Van Asbroeck 	/*
945308ee87aSSven Van Asbroeck 	 * reset the event cause bits.
946308ee87aSSven Van Asbroeck 	 * this must be done while owning the fbctrl area, so we'll
947308ee87aSSven Van Asbroeck 	 * enqueue a task to do that.
948308ee87aSSven Van Asbroeck 	 */
949308ee87aSSven Van Asbroeck 	t = create_area_writer(cd->qcache, IND_AX_FBCTRL,
950308ee87aSSven Van Asbroeck 			       REG_EVENT_CAUSE, &zero, sizeof(zero));
951308ee87aSSven Van Asbroeck 	if (!t) {
952308ee87aSSven Van Asbroeck 		ret = -ENOMEM;
953308ee87aSSven Van Asbroeck 		goto out;
954308ee87aSSven Van Asbroeck 	}
955308ee87aSSven Van Asbroeck 	t->done_fn = softint_ack;
956308ee87aSSven Van Asbroeck 	ret = ab_task_enqueue(t, cd->powerq, &cd->qlock, &cd->wq);
957308ee87aSSven Van Asbroeck 	ab_task_put(t);
958308ee87aSSven Van Asbroeck 	cd->softint_pending = true;
959308ee87aSSven Van Asbroeck out:
960308ee87aSSven Van Asbroeck 	WARN_ON(ret);
961308ee87aSSven Van Asbroeck 	if (ret)
962308ee87aSSven Van Asbroeck 		softint_ack(cd);
963308ee87aSSven Van Asbroeck }
964308ee87aSSven Van Asbroeck 
qthread_fn(void * data)965308ee87aSSven Van Asbroeck static int qthread_fn(void *data)
966308ee87aSSven Van Asbroeck {
967308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = data;
968308ee87aSSven Van Asbroeck 	struct kfifo *qs = cd->qs;
969308ee87aSSven Van Asbroeck 	size_t nqs = ARRAY_SIZE(cd->qs);
970308ee87aSSven Van Asbroeck 	unsigned int ind_ab;
971308ee87aSSven Van Asbroeck 
972308ee87aSSven Van Asbroeck 	/*
973308ee87aSSven Van Asbroeck 	 * this kernel thread has exclusive access to the anybus's memory.
974308ee87aSSven Van Asbroeck 	 * only exception: the IND_AB register, which is accessed exclusively
975308ee87aSSven Van Asbroeck 	 * by the interrupt service routine (ISR). This thread must not touch
976308ee87aSSven Van Asbroeck 	 * the IND_AB register, but it does require access to its value.
977308ee87aSSven Van Asbroeck 	 *
978308ee87aSSven Van Asbroeck 	 * the interrupt service routine stores the register's value in
979308ee87aSSven Van Asbroeck 	 * cd->ind_ab (an atomic_t), where we may safely access it, with the
980308ee87aSSven Van Asbroeck 	 * understanding that it can be modified by the ISR at any time.
981308ee87aSSven Van Asbroeck 	 */
982308ee87aSSven Van Asbroeck 
983308ee87aSSven Van Asbroeck 	while (!kthread_should_stop()) {
984308ee87aSSven Van Asbroeck 		/*
985308ee87aSSven Van Asbroeck 		 * make a local copy of IND_AB, so we can go around the loop
986308ee87aSSven Van Asbroeck 		 * again in case it changed while processing queues and softint.
987308ee87aSSven Van Asbroeck 		 */
988308ee87aSSven Van Asbroeck 		ind_ab = atomic_read(&cd->ind_ab);
989308ee87aSSven Van Asbroeck 		process_qs(cd);
990308ee87aSSven Van Asbroeck 		process_softint(cd);
991308ee87aSSven Van Asbroeck 		wait_event_timeout(cd->wq,
992308ee87aSSven Van Asbroeck 				   (atomic_read(&cd->ind_ab) != ind_ab) ||
993308ee87aSSven Van Asbroeck 				qs_have_work(qs, nqs) ||
994308ee87aSSven Van Asbroeck 				kthread_should_stop(),
995308ee87aSSven Van Asbroeck 			HZ);
996308ee87aSSven Van Asbroeck 		/*
997308ee87aSSven Van Asbroeck 		 * time out so even 'stuck' tasks will run eventually,
998308ee87aSSven Van Asbroeck 		 * and can time out.
999308ee87aSSven Van Asbroeck 		 */
1000308ee87aSSven Van Asbroeck 	}
1001308ee87aSSven Van Asbroeck 
1002308ee87aSSven Van Asbroeck 	return 0;
1003308ee87aSSven Van Asbroeck }
1004308ee87aSSven Van Asbroeck 
1005308ee87aSSven Van Asbroeck /* ------------------------ anybus exports ------------------------ */
1006308ee87aSSven Van Asbroeck 
anybuss_start_init(struct anybuss_client * client,const struct anybuss_memcfg * cfg)1007308ee87aSSven Van Asbroeck int anybuss_start_init(struct anybuss_client *client,
1008308ee87aSSven Van Asbroeck 		       const struct anybuss_memcfg *cfg)
1009308ee87aSSven Van Asbroeck {
1010308ee87aSSven Van Asbroeck 	int ret;
1011308ee87aSSven Van Asbroeck 	u16 op_mode;
1012308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = client->host;
1013308ee87aSSven Van Asbroeck 	struct msg_anybus_init msg = {
1014308ee87aSSven Van Asbroeck 		.input_io_len = cpu_to_be16(cfg->input_io),
1015308ee87aSSven Van Asbroeck 		.input_dpram_len = cpu_to_be16(cfg->input_dpram),
1016308ee87aSSven Van Asbroeck 		.input_total_len = cpu_to_be16(cfg->input_total),
1017308ee87aSSven Van Asbroeck 		.output_io_len = cpu_to_be16(cfg->output_io),
1018308ee87aSSven Van Asbroeck 		.output_dpram_len = cpu_to_be16(cfg->output_dpram),
1019308ee87aSSven Van Asbroeck 		.output_total_len = cpu_to_be16(cfg->output_total),
1020308ee87aSSven Van Asbroeck 		.notif_config = cpu_to_be16(0x000F),
1021308ee87aSSven Van Asbroeck 		.wd_val = cpu_to_be16(0),
1022308ee87aSSven Van Asbroeck 	};
1023308ee87aSSven Van Asbroeck 
1024308ee87aSSven Van Asbroeck 	switch (cfg->offl_mode) {
10259cc05ed4SSven Van Asbroeck 	case FIELDBUS_DEV_OFFL_MODE_CLEAR:
1026308ee87aSSven Van Asbroeck 		op_mode = 0;
1027308ee87aSSven Van Asbroeck 		break;
10289cc05ed4SSven Van Asbroeck 	case FIELDBUS_DEV_OFFL_MODE_FREEZE:
1029308ee87aSSven Van Asbroeck 		op_mode = OP_MODE_FBFC;
1030308ee87aSSven Van Asbroeck 		break;
10319cc05ed4SSven Van Asbroeck 	case FIELDBUS_DEV_OFFL_MODE_SET:
1032308ee87aSSven Van Asbroeck 		op_mode = OP_MODE_FBS;
1033308ee87aSSven Van Asbroeck 		break;
1034308ee87aSSven Van Asbroeck 	default:
1035308ee87aSSven Van Asbroeck 		return -EINVAL;
1036308ee87aSSven Van Asbroeck 	}
1037308ee87aSSven Van Asbroeck 	msg.op_mode = cpu_to_be16(op_mode | OP_MODE_CD);
1038308ee87aSSven Van Asbroeck 	ret = _anybus_mbox_cmd(cd, CMD_START_INIT, false, NULL, 0,
1039308ee87aSSven Van Asbroeck 			       NULL, 0, NULL, 0);
1040308ee87aSSven Van Asbroeck 	if (ret)
1041308ee87aSSven Van Asbroeck 		return ret;
1042308ee87aSSven Van Asbroeck 	return _anybus_mbox_cmd(cd, CMD_ANYBUS_INIT, false,
1043308ee87aSSven Van Asbroeck 			&msg, sizeof(msg), NULL, 0, NULL, 0);
1044308ee87aSSven Van Asbroeck }
1045308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_start_init);
1046308ee87aSSven Van Asbroeck 
anybuss_finish_init(struct anybuss_client * client)1047308ee87aSSven Van Asbroeck int anybuss_finish_init(struct anybuss_client *client)
1048308ee87aSSven Van Asbroeck {
1049308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = client->host;
1050308ee87aSSven Van Asbroeck 
1051308ee87aSSven Van Asbroeck 	return _anybus_mbox_cmd(cd, CMD_END_INIT, false, NULL, 0,
1052308ee87aSSven Van Asbroeck 					NULL, 0, NULL, 0);
1053308ee87aSSven Van Asbroeck }
1054308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_finish_init);
1055308ee87aSSven Van Asbroeck 
anybuss_read_fbctrl(struct anybuss_client * client,u16 addr,void * buf,size_t count)1056308ee87aSSven Van Asbroeck int anybuss_read_fbctrl(struct anybuss_client *client, u16 addr,
1057308ee87aSSven Van Asbroeck 			void *buf, size_t count)
1058308ee87aSSven Van Asbroeck {
1059308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = client->host;
1060308ee87aSSven Van Asbroeck 	struct ab_task *t;
1061308ee87aSSven Van Asbroeck 	int ret;
1062308ee87aSSven Van Asbroeck 
1063308ee87aSSven Van Asbroeck 	if (count == 0)
1064308ee87aSSven Van Asbroeck 		return 0;
1065308ee87aSSven Van Asbroeck 	if (!area_range_ok(addr, count, FBCTRL_AREA,
1066308ee87aSSven Van Asbroeck 			   MAX_FBCTRL_AREA_SZ))
1067308ee87aSSven Van Asbroeck 		return -EFAULT;
1068308ee87aSSven Van Asbroeck 	t = create_area_reader(cd->qcache, IND_AX_FBCTRL, addr, count);
1069308ee87aSSven Van Asbroeck 	if (!t)
1070308ee87aSSven Van Asbroeck 		return -ENOMEM;
1071308ee87aSSven Van Asbroeck 	ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
1072308ee87aSSven Van Asbroeck 	if (ret)
1073308ee87aSSven Van Asbroeck 		goto out;
1074308ee87aSSven Van Asbroeck 	memcpy(buf, t->area_pd.buf, count);
1075308ee87aSSven Van Asbroeck out:
1076308ee87aSSven Van Asbroeck 	ab_task_put(t);
1077308ee87aSSven Van Asbroeck 	return ret;
1078308ee87aSSven Van Asbroeck }
1079308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_read_fbctrl);
1080308ee87aSSven Van Asbroeck 
anybuss_write_input(struct anybuss_client * client,const char __user * buf,size_t size,loff_t * offset)1081308ee87aSSven Van Asbroeck int anybuss_write_input(struct anybuss_client *client,
1082308ee87aSSven Van Asbroeck 			const char __user *buf, size_t size,
1083308ee87aSSven Van Asbroeck 				loff_t *offset)
1084308ee87aSSven Van Asbroeck {
1085308ee87aSSven Van Asbroeck 	ssize_t len = min_t(loff_t, MAX_DATA_AREA_SZ - *offset, size);
1086308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = client->host;
1087308ee87aSSven Van Asbroeck 	struct ab_task *t;
1088308ee87aSSven Van Asbroeck 	int ret;
1089308ee87aSSven Van Asbroeck 
1090308ee87aSSven Van Asbroeck 	if (len <= 0)
1091308ee87aSSven Van Asbroeck 		return 0;
1092308ee87aSSven Van Asbroeck 	t = create_area_user_writer(cd->qcache, IND_AX_IN,
1093308ee87aSSven Van Asbroeck 				    DATA_IN_AREA + *offset, buf, len);
1094308ee87aSSven Van Asbroeck 	if (IS_ERR(t))
1095308ee87aSSven Van Asbroeck 		return PTR_ERR(t);
1096308ee87aSSven Van Asbroeck 	ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
1097308ee87aSSven Van Asbroeck 	ab_task_put(t);
1098308ee87aSSven Van Asbroeck 	if (ret)
1099308ee87aSSven Van Asbroeck 		return ret;
1100308ee87aSSven Van Asbroeck 	/* success */
1101308ee87aSSven Van Asbroeck 	*offset += len;
1102308ee87aSSven Van Asbroeck 	return len;
1103308ee87aSSven Van Asbroeck }
1104308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_write_input);
1105308ee87aSSven Van Asbroeck 
anybuss_read_output(struct anybuss_client * client,char __user * buf,size_t size,loff_t * offset)1106308ee87aSSven Van Asbroeck int anybuss_read_output(struct anybuss_client *client,
1107308ee87aSSven Van Asbroeck 			char __user *buf, size_t size,
1108308ee87aSSven Van Asbroeck 				loff_t *offset)
1109308ee87aSSven Van Asbroeck {
1110308ee87aSSven Van Asbroeck 	ssize_t len = min_t(loff_t, MAX_DATA_AREA_SZ - *offset, size);
1111308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = client->host;
1112308ee87aSSven Van Asbroeck 	struct ab_task *t;
1113308ee87aSSven Van Asbroeck 	int ret;
1114308ee87aSSven Van Asbroeck 
1115308ee87aSSven Van Asbroeck 	if (len <= 0)
1116308ee87aSSven Van Asbroeck 		return 0;
1117308ee87aSSven Van Asbroeck 	t = create_area_reader(cd->qcache, IND_AX_OUT,
1118308ee87aSSven Van Asbroeck 			       DATA_OUT_AREA + *offset, len);
1119308ee87aSSven Van Asbroeck 	if (!t)
1120308ee87aSSven Van Asbroeck 		return -ENOMEM;
1121308ee87aSSven Van Asbroeck 	ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
1122308ee87aSSven Van Asbroeck 	if (ret)
1123308ee87aSSven Van Asbroeck 		goto out;
1124308ee87aSSven Van Asbroeck 	if (copy_to_user(buf, t->area_pd.buf, len))
1125308ee87aSSven Van Asbroeck 		ret = -EFAULT;
1126308ee87aSSven Van Asbroeck out:
1127308ee87aSSven Van Asbroeck 	ab_task_put(t);
1128308ee87aSSven Van Asbroeck 	if (ret)
1129308ee87aSSven Van Asbroeck 		return ret;
1130308ee87aSSven Van Asbroeck 	/* success */
1131308ee87aSSven Van Asbroeck 	*offset += len;
1132308ee87aSSven Van Asbroeck 	return len;
1133308ee87aSSven Van Asbroeck }
1134308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_read_output);
1135308ee87aSSven Van Asbroeck 
anybuss_send_msg(struct anybuss_client * client,u16 cmd_num,const void * buf,size_t count)1136308ee87aSSven Van Asbroeck int anybuss_send_msg(struct anybuss_client *client, u16 cmd_num,
1137308ee87aSSven Van Asbroeck 		     const void *buf, size_t count)
1138308ee87aSSven Van Asbroeck {
1139308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = client->host;
1140308ee87aSSven Van Asbroeck 
1141308ee87aSSven Van Asbroeck 	return _anybus_mbox_cmd(cd, cmd_num, true, buf, count, NULL, 0,
1142308ee87aSSven Van Asbroeck 					NULL, 0);
1143308ee87aSSven Van Asbroeck }
1144308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_send_msg);
1145308ee87aSSven Van Asbroeck 
anybuss_send_ext(struct anybuss_client * client,u16 cmd_num,const void * buf,size_t count)1146308ee87aSSven Van Asbroeck int anybuss_send_ext(struct anybuss_client *client, u16 cmd_num,
1147308ee87aSSven Van Asbroeck 		     const void *buf, size_t count)
1148308ee87aSSven Van Asbroeck {
1149308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = client->host;
1150308ee87aSSven Van Asbroeck 
1151308ee87aSSven Van Asbroeck 	return _anybus_mbox_cmd(cd, cmd_num, true, NULL, 0, NULL, 0,
1152308ee87aSSven Van Asbroeck 					buf, count);
1153308ee87aSSven Van Asbroeck }
1154308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_send_ext);
1155308ee87aSSven Van Asbroeck 
anybuss_recv_msg(struct anybuss_client * client,u16 cmd_num,void * buf,size_t count)1156308ee87aSSven Van Asbroeck int anybuss_recv_msg(struct anybuss_client *client, u16 cmd_num,
1157308ee87aSSven Van Asbroeck 		     void *buf, size_t count)
1158308ee87aSSven Van Asbroeck {
1159308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = client->host;
1160308ee87aSSven Van Asbroeck 
1161308ee87aSSven Van Asbroeck 	return _anybus_mbox_cmd(cd, cmd_num, true, NULL, 0, buf, count,
1162308ee87aSSven Van Asbroeck 					NULL, 0);
1163308ee87aSSven Van Asbroeck }
1164308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_recv_msg);
1165308ee87aSSven Van Asbroeck 
1166308ee87aSSven Van Asbroeck /* ------------------------ bus functions ------------------------ */
1167308ee87aSSven Van Asbroeck 
anybus_bus_match(struct device * dev,struct device_driver * drv)1168308ee87aSSven Van Asbroeck static int anybus_bus_match(struct device *dev,
1169308ee87aSSven Van Asbroeck 			    struct device_driver *drv)
1170308ee87aSSven Van Asbroeck {
1171308ee87aSSven Van Asbroeck 	struct anybuss_client_driver *adrv =
1172308ee87aSSven Van Asbroeck 		to_anybuss_client_driver(drv);
1173308ee87aSSven Van Asbroeck 	struct anybuss_client *adev =
1174308ee87aSSven Van Asbroeck 		to_anybuss_client(dev);
1175308ee87aSSven Van Asbroeck 
117639e80462SSven Van Asbroeck 	return adrv->anybus_id == be16_to_cpu(adev->anybus_id);
1177308ee87aSSven Van Asbroeck }
1178308ee87aSSven Van Asbroeck 
anybus_bus_probe(struct device * dev)1179308ee87aSSven Van Asbroeck static int anybus_bus_probe(struct device *dev)
1180308ee87aSSven Van Asbroeck {
1181308ee87aSSven Van Asbroeck 	struct anybuss_client_driver *adrv =
1182308ee87aSSven Van Asbroeck 		to_anybuss_client_driver(dev->driver);
1183308ee87aSSven Van Asbroeck 	struct anybuss_client *adev =
1184308ee87aSSven Van Asbroeck 		to_anybuss_client(dev);
1185308ee87aSSven Van Asbroeck 
1186308ee87aSSven Van Asbroeck 	return adrv->probe(adev);
1187308ee87aSSven Van Asbroeck }
1188308ee87aSSven Van Asbroeck 
anybus_bus_remove(struct device * dev)1189fc7a6209SUwe Kleine-König static void anybus_bus_remove(struct device *dev)
1190308ee87aSSven Van Asbroeck {
1191308ee87aSSven Van Asbroeck 	struct anybuss_client_driver *adrv =
1192308ee87aSSven Van Asbroeck 		to_anybuss_client_driver(dev->driver);
1193308ee87aSSven Van Asbroeck 
1194308ee87aSSven Van Asbroeck 	if (adrv->remove)
119532dcd072SUwe Kleine-König 		adrv->remove(to_anybuss_client(dev));
1196308ee87aSSven Van Asbroeck }
1197308ee87aSSven Van Asbroeck 
1198308ee87aSSven Van Asbroeck static struct bus_type anybus_bus = {
1199308ee87aSSven Van Asbroeck 	.name		= "anybuss",
1200308ee87aSSven Van Asbroeck 	.match		= anybus_bus_match,
1201308ee87aSSven Van Asbroeck 	.probe		= anybus_bus_probe,
1202308ee87aSSven Van Asbroeck 	.remove		= anybus_bus_remove,
1203308ee87aSSven Van Asbroeck };
1204308ee87aSSven Van Asbroeck 
anybuss_client_driver_register(struct anybuss_client_driver * drv)1205308ee87aSSven Van Asbroeck int anybuss_client_driver_register(struct anybuss_client_driver *drv)
1206308ee87aSSven Van Asbroeck {
1207a3417158SUwe Kleine-König 	if (!drv->probe)
1208a3417158SUwe Kleine-König 		return -ENODEV;
1209a3417158SUwe Kleine-König 
1210308ee87aSSven Van Asbroeck 	drv->driver.bus = &anybus_bus;
1211308ee87aSSven Van Asbroeck 	return driver_register(&drv->driver);
1212308ee87aSSven Van Asbroeck }
1213308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_client_driver_register);
1214308ee87aSSven Van Asbroeck 
anybuss_client_driver_unregister(struct anybuss_client_driver * drv)1215308ee87aSSven Van Asbroeck void anybuss_client_driver_unregister(struct anybuss_client_driver *drv)
1216308ee87aSSven Van Asbroeck {
1217308ee87aSSven Van Asbroeck 	return driver_unregister(&drv->driver);
1218308ee87aSSven Van Asbroeck }
1219308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_client_driver_unregister);
1220308ee87aSSven Van Asbroeck 
client_device_release(struct device * dev)1221308ee87aSSven Van Asbroeck static void client_device_release(struct device *dev)
1222308ee87aSSven Van Asbroeck {
1223308ee87aSSven Van Asbroeck 	kfree(to_anybuss_client(dev));
1224308ee87aSSven Van Asbroeck }
1225308ee87aSSven Van Asbroeck 
taskq_alloc(struct device * dev,struct kfifo * q)1226308ee87aSSven Van Asbroeck static int taskq_alloc(struct device *dev, struct kfifo *q)
1227308ee87aSSven Van Asbroeck {
1228308ee87aSSven Van Asbroeck 	void *buf;
1229308ee87aSSven Van Asbroeck 	size_t size = 64 * sizeof(struct ab_task *);
1230308ee87aSSven Van Asbroeck 
1231308ee87aSSven Van Asbroeck 	buf = devm_kzalloc(dev, size, GFP_KERNEL);
1232308ee87aSSven Van Asbroeck 	if (!buf)
1233308ee87aSSven Van Asbroeck 		return -EIO;
1234308ee87aSSven Van Asbroeck 	return kfifo_init(q, buf, size);
1235308ee87aSSven Van Asbroeck }
1236308ee87aSSven Van Asbroeck 
anybus_of_get_host_idx(struct device_node * np)1237308ee87aSSven Van Asbroeck static int anybus_of_get_host_idx(struct device_node *np)
1238308ee87aSSven Van Asbroeck {
1239308ee87aSSven Van Asbroeck 	const __be32 *host_idx;
1240308ee87aSSven Van Asbroeck 
1241308ee87aSSven Van Asbroeck 	host_idx = of_get_address(np, 0, NULL, NULL);
1242308ee87aSSven Van Asbroeck 	if (!host_idx)
1243308ee87aSSven Van Asbroeck 		return -ENOENT;
1244308ee87aSSven Van Asbroeck 	return __be32_to_cpu(*host_idx);
1245308ee87aSSven Van Asbroeck }
1246308ee87aSSven Van Asbroeck 
1247308ee87aSSven Van Asbroeck static struct device_node *
anybus_of_find_child_device(struct device * dev,int host_idx)1248308ee87aSSven Van Asbroeck anybus_of_find_child_device(struct device *dev, int host_idx)
1249308ee87aSSven Van Asbroeck {
1250308ee87aSSven Van Asbroeck 	struct device_node *node;
1251308ee87aSSven Van Asbroeck 
1252308ee87aSSven Van Asbroeck 	if (!dev || !dev->of_node)
1253308ee87aSSven Van Asbroeck 		return NULL;
1254308ee87aSSven Van Asbroeck 	for_each_child_of_node(dev->of_node, node) {
1255308ee87aSSven Van Asbroeck 		if (anybus_of_get_host_idx(node) == host_idx)
1256308ee87aSSven Van Asbroeck 			return node;
1257308ee87aSSven Van Asbroeck 	}
1258308ee87aSSven Van Asbroeck 	return NULL;
1259308ee87aSSven Van Asbroeck }
1260308ee87aSSven Van Asbroeck 
1261308ee87aSSven Van Asbroeck struct anybuss_host * __must_check
anybuss_host_common_probe(struct device * dev,const struct anybuss_ops * ops)1262308ee87aSSven Van Asbroeck anybuss_host_common_probe(struct device *dev,
1263308ee87aSSven Van Asbroeck 			  const struct anybuss_ops *ops)
1264308ee87aSSven Van Asbroeck {
1265308ee87aSSven Van Asbroeck 	int ret, i;
1266308ee87aSSven Van Asbroeck 	u8 val[4];
1267be7d6b03SSven Van Asbroeck 	__be16 fieldbus_type;
1268308ee87aSSven Van Asbroeck 	struct anybuss_host *cd;
1269308ee87aSSven Van Asbroeck 
1270308ee87aSSven Van Asbroeck 	cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL);
1271308ee87aSSven Van Asbroeck 	if (!cd)
1272308ee87aSSven Van Asbroeck 		return ERR_PTR(-ENOMEM);
1273308ee87aSSven Van Asbroeck 	cd->dev = dev;
1274308ee87aSSven Van Asbroeck 	cd->host_idx = ops->host_idx;
1275308ee87aSSven Van Asbroeck 	init_completion(&cd->card_boot);
1276308ee87aSSven Van Asbroeck 	init_waitqueue_head(&cd->wq);
1277308ee87aSSven Van Asbroeck 	for (i = 0; i < ARRAY_SIZE(cd->qs); i++) {
1278308ee87aSSven Van Asbroeck 		ret = taskq_alloc(dev, &cd->qs[i]);
1279308ee87aSSven Van Asbroeck 		if (ret)
1280308ee87aSSven Van Asbroeck 			return ERR_PTR(ret);
1281308ee87aSSven Van Asbroeck 	}
1282308ee87aSSven Van Asbroeck 	if (WARN_ON(ARRAY_SIZE(cd->qs) < 3))
1283308ee87aSSven Van Asbroeck 		return ERR_PTR(-EINVAL);
1284308ee87aSSven Van Asbroeck 	cd->powerq = &cd->qs[0];
1285308ee87aSSven Van Asbroeck 	cd->mboxq = &cd->qs[1];
1286308ee87aSSven Van Asbroeck 	cd->areaq = &cd->qs[2];
1287308ee87aSSven Van Asbroeck 	cd->reset = ops->reset;
1288308ee87aSSven Van Asbroeck 	if (!cd->reset)
1289308ee87aSSven Van Asbroeck 		return ERR_PTR(-EINVAL);
1290308ee87aSSven Van Asbroeck 	cd->regmap = ops->regmap;
1291308ee87aSSven Van Asbroeck 	if (!cd->regmap)
1292308ee87aSSven Van Asbroeck 		return ERR_PTR(-EINVAL);
1293308ee87aSSven Van Asbroeck 	spin_lock_init(&cd->qlock);
1294308ee87aSSven Van Asbroeck 	cd->qcache = kmem_cache_create(dev_name(dev),
1295308ee87aSSven Van Asbroeck 				       sizeof(struct ab_task), 0, 0, NULL);
1296308ee87aSSven Van Asbroeck 	if (!cd->qcache)
1297308ee87aSSven Van Asbroeck 		return ERR_PTR(-ENOMEM);
1298308ee87aSSven Van Asbroeck 	cd->irq = ops->irq;
1299308ee87aSSven Van Asbroeck 	if (cd->irq <= 0) {
1300308ee87aSSven Van Asbroeck 		ret = -EINVAL;
1301308ee87aSSven Van Asbroeck 		goto err_qcache;
1302308ee87aSSven Van Asbroeck 	}
1303308ee87aSSven Van Asbroeck 	/*
1304308ee87aSSven Van Asbroeck 	 * use a dpram test to check if a card is present, this is only
1305308ee87aSSven Van Asbroeck 	 * possible while in reset.
1306308ee87aSSven Van Asbroeck 	 */
1307308ee87aSSven Van Asbroeck 	reset_assert(cd);
1308308ee87aSSven Van Asbroeck 	if (test_dpram(cd->regmap)) {
1309308ee87aSSven Van Asbroeck 		dev_err(dev, "no Anybus-S card in slot");
1310308ee87aSSven Van Asbroeck 		ret = -ENODEV;
1311308ee87aSSven Van Asbroeck 		goto err_qcache;
1312308ee87aSSven Van Asbroeck 	}
1313308ee87aSSven Van Asbroeck 	ret = devm_request_threaded_irq(dev, cd->irq, NULL, irq_handler,
1314308ee87aSSven Van Asbroeck 					IRQF_ONESHOT, dev_name(dev), cd);
1315308ee87aSSven Van Asbroeck 	if (ret) {
1316308ee87aSSven Van Asbroeck 		dev_err(dev, "could not request irq");
1317308ee87aSSven Van Asbroeck 		goto err_qcache;
1318308ee87aSSven Van Asbroeck 	}
1319308ee87aSSven Van Asbroeck 	/*
1320308ee87aSSven Van Asbroeck 	 * startup sequence:
1321bdcfac6aSAjith P V 	 *   a) perform dummy IND_AB read to prevent false 'init done' irq
1322308ee87aSSven Van Asbroeck 	 *     (already done by test_dpram() above)
1323bdcfac6aSAjith P V 	 *   b) release reset
1324bdcfac6aSAjith P V 	 *   c) wait for first interrupt
1325bdcfac6aSAjith P V 	 *   d) interrupt came in: ready to go !
1326308ee87aSSven Van Asbroeck 	 */
1327308ee87aSSven Van Asbroeck 	reset_deassert(cd);
13283e2e9cf9SNicholas Mc Guire 	if (!wait_for_completion_timeout(&cd->card_boot, TIMEOUT)) {
1329308ee87aSSven Van Asbroeck 		ret = -ETIMEDOUT;
1330308ee87aSSven Van Asbroeck 		goto err_reset;
13313e2e9cf9SNicholas Mc Guire 	}
1332308ee87aSSven Van Asbroeck 	/*
1333308ee87aSSven Van Asbroeck 	 * according to the anybus docs, we're allowed to read these
1334308ee87aSSven Van Asbroeck 	 * without handshaking / reserving the area
1335308ee87aSSven Van Asbroeck 	 */
1336308ee87aSSven Van Asbroeck 	dev_info(dev, "Anybus-S card detected");
1337308ee87aSSven Van Asbroeck 	regmap_bulk_read(cd->regmap, REG_BOOTLOADER_V, val, 2);
1338308ee87aSSven Van Asbroeck 	dev_info(dev, "Bootloader version: %02X%02X",
1339308ee87aSSven Van Asbroeck 		 val[0], val[1]);
1340308ee87aSSven Van Asbroeck 	regmap_bulk_read(cd->regmap, REG_API_V, val, 2);
1341308ee87aSSven Van Asbroeck 	dev_info(dev, "API version: %02X%02X", val[0], val[1]);
1342308ee87aSSven Van Asbroeck 	regmap_bulk_read(cd->regmap, REG_FIELDBUS_V, val, 2);
1343308ee87aSSven Van Asbroeck 	dev_info(dev, "Fieldbus version: %02X%02X", val[0], val[1]);
1344308ee87aSSven Van Asbroeck 	regmap_bulk_read(cd->regmap, REG_SERIAL_NO, val, 4);
1345308ee87aSSven Van Asbroeck 	dev_info(dev, "Serial number: %02X%02X%02X%02X",
1346308ee87aSSven Van Asbroeck 		 val[0], val[1], val[2], val[3]);
1347308ee87aSSven Van Asbroeck 	add_device_randomness(&val, 4);
1348308ee87aSSven Van Asbroeck 	regmap_bulk_read(cd->regmap, REG_FIELDBUS_TYPE, &fieldbus_type,
1349308ee87aSSven Van Asbroeck 			 sizeof(fieldbus_type));
1350be7d6b03SSven Van Asbroeck 	dev_info(dev, "Fieldbus type: %04X", be16_to_cpu(fieldbus_type));
1351308ee87aSSven Van Asbroeck 	regmap_bulk_read(cd->regmap, REG_MODULE_SW_V, val, 2);
1352308ee87aSSven Van Asbroeck 	dev_info(dev, "Module SW version: %02X%02X",
1353308ee87aSSven Van Asbroeck 		 val[0], val[1]);
1354308ee87aSSven Van Asbroeck 	/* put card back reset until a client driver releases it */
1355308ee87aSSven Van Asbroeck 	disable_irq(cd->irq);
1356308ee87aSSven Van Asbroeck 	reset_assert(cd);
1357308ee87aSSven Van Asbroeck 	atomic_set(&cd->ind_ab, IND_AB_UPDATED);
1358308ee87aSSven Van Asbroeck 	/* fire up the queue thread */
1359308ee87aSSven Van Asbroeck 	cd->qthread = kthread_run(qthread_fn, cd, dev_name(dev));
1360308ee87aSSven Van Asbroeck 	if (IS_ERR(cd->qthread)) {
1361308ee87aSSven Van Asbroeck 		dev_err(dev, "could not create kthread");
1362308ee87aSSven Van Asbroeck 		ret = PTR_ERR(cd->qthread);
1363308ee87aSSven Van Asbroeck 		goto err_reset;
1364308ee87aSSven Van Asbroeck 	}
1365308ee87aSSven Van Asbroeck 	/*
1366308ee87aSSven Van Asbroeck 	 * now advertise that we've detected a client device (card).
1367308ee87aSSven Van Asbroeck 	 * the bus infrastructure will match it to a client driver.
1368308ee87aSSven Van Asbroeck 	 */
1369308ee87aSSven Van Asbroeck 	cd->client = kzalloc(sizeof(*cd->client), GFP_KERNEL);
1370308ee87aSSven Van Asbroeck 	if (!cd->client) {
1371308ee87aSSven Van Asbroeck 		ret = -ENOMEM;
1372308ee87aSSven Van Asbroeck 		goto err_kthread;
1373308ee87aSSven Van Asbroeck 	}
137439e80462SSven Van Asbroeck 	cd->client->anybus_id = fieldbus_type;
1375308ee87aSSven Van Asbroeck 	cd->client->host = cd;
1376308ee87aSSven Van Asbroeck 	cd->client->dev.bus = &anybus_bus;
1377308ee87aSSven Van Asbroeck 	cd->client->dev.parent = dev;
1378308ee87aSSven Van Asbroeck 	cd->client->dev.release = client_device_release;
1379308ee87aSSven Van Asbroeck 	cd->client->dev.of_node =
1380308ee87aSSven Van Asbroeck 		anybus_of_find_child_device(dev, cd->host_idx);
1381308ee87aSSven Van Asbroeck 	dev_set_name(&cd->client->dev, "anybuss.card%d", cd->host_idx);
1382308ee87aSSven Van Asbroeck 	ret = device_register(&cd->client->dev);
1383308ee87aSSven Van Asbroeck 	if (ret)
1384308ee87aSSven Van Asbroeck 		goto err_device;
1385308ee87aSSven Van Asbroeck 	return cd;
1386308ee87aSSven Van Asbroeck err_device:
1387*7079b348SChristophe JAILLET 	put_device(&cd->client->dev);
1388308ee87aSSven Van Asbroeck err_kthread:
1389308ee87aSSven Van Asbroeck 	kthread_stop(cd->qthread);
1390308ee87aSSven Van Asbroeck err_reset:
1391308ee87aSSven Van Asbroeck 	reset_assert(cd);
1392308ee87aSSven Van Asbroeck err_qcache:
1393308ee87aSSven Van Asbroeck 	kmem_cache_destroy(cd->qcache);
1394308ee87aSSven Van Asbroeck 	return ERR_PTR(ret);
1395308ee87aSSven Van Asbroeck }
1396308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_host_common_probe);
1397308ee87aSSven Van Asbroeck 
anybuss_host_common_remove(struct anybuss_host * host)1398308ee87aSSven Van Asbroeck void anybuss_host_common_remove(struct anybuss_host *host)
1399308ee87aSSven Van Asbroeck {
1400308ee87aSSven Van Asbroeck 	struct anybuss_host *cd = host;
1401308ee87aSSven Van Asbroeck 
1402308ee87aSSven Van Asbroeck 	device_unregister(&cd->client->dev);
1403308ee87aSSven Van Asbroeck 	kthread_stop(cd->qthread);
1404308ee87aSSven Van Asbroeck 	reset_assert(cd);
1405308ee87aSSven Van Asbroeck 	kmem_cache_destroy(cd->qcache);
1406308ee87aSSven Van Asbroeck }
1407308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(anybuss_host_common_remove);
1408308ee87aSSven Van Asbroeck 
host_release(void * res)140907ff20cfSTian Tao static void host_release(void *res)
1410308ee87aSSven Van Asbroeck {
141107ff20cfSTian Tao 	anybuss_host_common_remove(res);
1412308ee87aSSven Van Asbroeck }
1413308ee87aSSven Van Asbroeck 
1414308ee87aSSven Van Asbroeck struct anybuss_host * __must_check
devm_anybuss_host_common_probe(struct device * dev,const struct anybuss_ops * ops)1415308ee87aSSven Van Asbroeck devm_anybuss_host_common_probe(struct device *dev,
1416308ee87aSSven Van Asbroeck 			       const struct anybuss_ops *ops)
1417308ee87aSSven Van Asbroeck {
1418308ee87aSSven Van Asbroeck 	struct anybuss_host *host;
141907ff20cfSTian Tao 	int ret;
1420308ee87aSSven Van Asbroeck 
1421308ee87aSSven Van Asbroeck 	host = anybuss_host_common_probe(dev, ops);
142207ff20cfSTian Tao 	if (IS_ERR(host))
1423308ee87aSSven Van Asbroeck 		return host;
142407ff20cfSTian Tao 
142507ff20cfSTian Tao 	ret = devm_add_action_or_reset(dev, host_release, host);
142607ff20cfSTian Tao 	if (ret)
142707ff20cfSTian Tao 		return ERR_PTR(ret);
142807ff20cfSTian Tao 
1429308ee87aSSven Van Asbroeck 	return host;
1430308ee87aSSven Van Asbroeck }
1431308ee87aSSven Van Asbroeck EXPORT_SYMBOL_GPL(devm_anybuss_host_common_probe);
1432308ee87aSSven Van Asbroeck 
anybus_init(void)1433308ee87aSSven Van Asbroeck static int __init anybus_init(void)
1434308ee87aSSven Van Asbroeck {
1435308ee87aSSven Van Asbroeck 	int ret;
1436308ee87aSSven Van Asbroeck 
1437308ee87aSSven Van Asbroeck 	ret = bus_register(&anybus_bus);
1438308ee87aSSven Van Asbroeck 	if (ret)
1439308ee87aSSven Van Asbroeck 		pr_err("could not register Anybus-S bus: %d\n", ret);
1440308ee87aSSven Van Asbroeck 	return ret;
1441308ee87aSSven Van Asbroeck }
1442308ee87aSSven Van Asbroeck module_init(anybus_init);
1443308ee87aSSven Van Asbroeck 
anybus_exit(void)1444308ee87aSSven Van Asbroeck static void __exit anybus_exit(void)
1445308ee87aSSven Van Asbroeck {
1446308ee87aSSven Van Asbroeck 	bus_unregister(&anybus_bus);
1447308ee87aSSven Van Asbroeck }
1448308ee87aSSven Van Asbroeck module_exit(anybus_exit);
1449308ee87aSSven Van Asbroeck 
1450308ee87aSSven Van Asbroeck MODULE_DESCRIPTION("HMS Anybus-S Host Driver");
1451308ee87aSSven Van Asbroeck MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
1452308ee87aSSven Van Asbroeck MODULE_LICENSE("GPL v2");
1453