1 /*
2  * Copyright (c) 2016, NVIDIA CORPORATION.
3  *
4  * SPDX-License-Identifier: GPL-2.0
5  */
6 
7 #include <common.h>
8 #include <dm.h>
9 #include <fdtdec.h>
10 #include <mailbox.h>
11 #include <mailbox-uclass.h>
12 
13 DECLARE_GLOBAL_DATA_PTR;
14 
15 static inline struct mbox_ops *mbox_dev_ops(struct udevice *dev)
16 {
17 	return (struct mbox_ops *)dev->driver->ops;
18 }
19 
20 static int mbox_of_xlate_default(struct mbox_chan *chan,
21 				 struct fdtdec_phandle_args *args)
22 {
23 	debug("%s(chan=%p)\n", __func__, chan);
24 
25 	if (args->args_count != 1) {
26 		debug("Invaild args_count: %d\n", args->args_count);
27 		return -EINVAL;
28 	}
29 
30 	chan->id = args->args[0];
31 
32 	return 0;
33 }
34 
35 int mbox_get_by_index(struct udevice *dev, int index, struct mbox_chan *chan)
36 {
37 	struct fdtdec_phandle_args args;
38 	int ret;
39 	struct udevice *dev_mbox;
40 	struct mbox_ops *ops;
41 
42 	debug("%s(dev=%p, index=%d, chan=%p)\n", __func__, dev, index, chan);
43 
44 	ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset,
45 					     "mboxes", "#mbox-cells", 0,
46 					     index, &args);
47 	if (ret) {
48 		debug("%s: fdtdec_parse_phandle_with_args failed: %d\n",
49 		      __func__, ret);
50 		return ret;
51 	}
52 
53 	ret = uclass_get_device_by_of_offset(UCLASS_MAILBOX, args.node,
54 					     &dev_mbox);
55 	if (ret) {
56 		debug("%s: uclass_get_device_by_of_offset failed: %d\n",
57 		      __func__, ret);
58 		return ret;
59 	}
60 	ops = mbox_dev_ops(dev_mbox);
61 
62 	chan->dev = dev_mbox;
63 	if (ops->of_xlate)
64 		ret = ops->of_xlate(chan, &args);
65 	else
66 		ret = mbox_of_xlate_default(chan, &args);
67 	if (ret) {
68 		debug("of_xlate() failed: %d\n", ret);
69 		return ret;
70 	}
71 
72 	ret = ops->request(chan);
73 	if (ret) {
74 		debug("ops->request() failed: %d\n", ret);
75 		return ret;
76 	}
77 
78 	return 0;
79 }
80 
81 int mbox_get_by_name(struct udevice *dev, const char *name,
82 		     struct mbox_chan *chan)
83 {
84 	int index;
85 
86 	debug("%s(dev=%p, name=%s, chan=%p)\n", __func__, dev, name, chan);
87 
88 	index = fdt_stringlist_search(gd->fdt_blob, dev->of_offset,
89 				      "mbox-names", name);
90 	if (index < 0) {
91 		debug("fdt_stringlist_search() failed: %d\n", index);
92 		return index;
93 	}
94 
95 	return mbox_get_by_index(dev, index, chan);
96 }
97 
98 int mbox_free(struct mbox_chan *chan)
99 {
100 	struct mbox_ops *ops = mbox_dev_ops(chan->dev);
101 
102 	debug("%s(chan=%p)\n", __func__, chan);
103 
104 	return ops->free(chan);
105 }
106 
107 int mbox_send(struct mbox_chan *chan, const void *data)
108 {
109 	struct mbox_ops *ops = mbox_dev_ops(chan->dev);
110 
111 	debug("%s(chan=%p, data=%p)\n", __func__, chan, data);
112 
113 	return ops->send(chan, data);
114 }
115 
116 int mbox_recv(struct mbox_chan *chan, void *data, ulong timeout_us)
117 {
118 	struct mbox_ops *ops = mbox_dev_ops(chan->dev);
119 	ulong start_time;
120 	int ret;
121 
122 	debug("%s(chan=%p, data=%p, timeout_us=%ld)\n", __func__, chan, data,
123 	      timeout_us);
124 
125 	start_time = timer_get_us();
126 	/*
127 	 * Account for partial us ticks, but if timeout_us is 0, ensure we
128 	 * still don't wait at all.
129 	 */
130 	if (timeout_us)
131 		timeout_us++;
132 
133 	for (;;) {
134 		ret = ops->recv(chan, data);
135 		if (ret != -ENODATA)
136 			return ret;
137 		if ((timer_get_us() - start_time) >= timeout_us)
138 			return -ETIMEDOUT;
139 	}
140 }
141 
142 UCLASS_DRIVER(mailbox) = {
143 	.id		= UCLASS_MAILBOX,
144 	.name		= "mailbox",
145 };
146