imx-mu.c
4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
* Copyright 2017 NXP
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <asm/io.h>
#include <dm.h>
#include <mailbox-uclass.h>
#define NUM_MU_CHANNELS 4
#define NUM_MU_FLAGS 4
#define NUM_MU_GIP 4
#define mu_rr(x) (0x10 + (x * 0x4))
#define mu_tr(x) (x * 0x4)
#define MU_SR_OFFSET 0x20
#define MU_CR_OFFSET 0x24
#define CHAN_TE_MASK(x) (0x00100000 << (x))
#define CHAN_RF_MASK(x) (0x01000000 << (x))
#define MU_CR_INT_MSK 0xFFF00000
#define MU_FLGS_MSK 0x00000007
#define MU_GIP_MSK 0xF0000000
/* This driver only exposes the status bits to keep with the
* polling methodology of u-boot.
*/
DECLARE_GLOBAL_DATA_PTR;
struct imx_mu_mbox {
fdt_addr_t base;
/* use pointers to channel as a way to reserve channels */
void *channels[NUM_MU_CHANNELS];
bool flags[NUM_MU_FLAGS];
/* TODO add support for the reading/setting of flags to
* B side of MU
*/
};
/* check that the channel is open or owned by caller */
static int mu_check_channel(struct mbox_chan *chan)
{
struct imx_mu_mbox *mailbox = dev_get_priv(chan->dev);
/* use id as number of channel within mbox only */
if ((chan->id < 0) || (chan->id >= NUM_MU_CHANNELS)) {
debug("nxp mu id out of range: %lu\n", chan->id);
return -EINVAL;
}
if (mailbox->channels[chan->id] != NULL) {
/* if reserved check that caller owns */
if (mailbox->channels[chan->id] == chan)
return 1; /* caller owns the channel */
return -EACCES;
}
return 0;/* channel empty */
}
static int mu_chan_request(struct mbox_chan *chan)
{
struct imx_mu_mbox *mailbox = dev_get_priv(chan->dev);
debug("%s(chan=%p)\n", __func__, chan);
int status = mu_check_channel(chan);
if (status < 0) {
debug("channel not available :%d\n", status);
return -EPERM;
}
mailbox->channels[chan->id] = chan;
return 0;
}
/* currently not dynamically allocated
* only change pointer back to NULL */
static int mu_chan_free(struct mbox_chan *chan)
{
struct imx_mu_mbox *mailbox = dev_get_priv(chan->dev);
int status = mu_check_channel(chan);
debug("%s(chan=%p)\n", __func__, chan);
if (status <= 0) { /* check that the channel is also not empty */
debug("mu_chan_free() failed exit code: %d\n", status);
return status;
}
/*if you own channel and channel is NOT empty */
mailbox->channels[chan->id] = NULL;
return 0;
}
static int mu_send(struct mbox_chan *chan, const void *data)
{
struct imx_mu_mbox *mbox = dev_get_priv(chan->dev);
int status = mu_check_channel(chan);
uint32_t val = *((uint32_t *)data);
debug("%s(chan=%p, data=%p)\n", __func__, chan, data);
if (status < 1) {
debug("mu_send() failed. mu_chan_status is :%d\n", status);
return -EPERM;
}
/*check if transmit register is empty */
if (!(readl(mbox->base+MU_SR_OFFSET) & CHAN_TE_MASK(chan->id)))
return -EBUSY;
/* send out on transmit register*/
writel(val, mbox->base + mu_tr(chan->id));
return 0;
}
static int mu_recv(struct mbox_chan *chan, void *data)
{
struct imx_mu_mbox *mbox = dev_get_priv(chan->dev);
int status = mu_check_channel(chan);
uint32_t *buffer = data;
debug("%s(chan=%p, data=%p)\n", __func__, chan, data);
if (status < 1)
return -EPERM; /* return if channel isnt owned */
if (readl(mbox->base + MU_SR_OFFSET) & CHAN_RF_MASK(chan->id))
return -ENODATA;
*buffer = readl(mu_rr(chan->id));
return 0;
}
static int imx_mu_bind(struct udevice *dev)
{
debug("%s(dev=%p)\n", __func__, dev);
return 0;
}
static int imx_mu_probe(struct udevice *dev)
{
struct imx_mu_mbox *mbox = dev_get_priv(dev);
uint32_t val;
debug("%s(dev=%p)\n", __func__, dev);
/* get address from device tree */
mbox->base = dev_get_addr(dev);
if (mbox->base == FDT_ADDR_T_NONE)
return -ENODEV;
val = readl(mbox->base + MU_CR_OFFSET);
val = val & ~MU_CR_INT_MSK;/* disable all interrupts */
val = val & ~MU_FLGS_MSK; /* clear all flags */
writel(val, mbox->base + MU_CR_OFFSET);
val = readl(mbox->base + MU_SR_OFFSET);
val = val | MU_GIP_MSK; /* clear any pending GIP */
writel(val, mbox->base + MU_SR_OFFSET);
return 0;
}
static const struct udevice_id imx_mu_ids[] = {
{ .compatible = "nxp,imx-mu" },
{ }
};
struct mbox_ops imx_mu_mbox_ops = {
.request = mu_chan_request,
.free = mu_chan_free,
.send = mu_send,
.recv = mu_recv,
};
U_BOOT_DRIVER(imx_mu) = {
.name = "imx-mu",
.id = UCLASS_MAILBOX,
.of_match = imx_mu_ids,
.bind = imx_mu_bind,
.probe = imx_mu_probe,
.priv_auto_alloc_size = sizeof(struct imx_mu_mbox),
.ops = &imx_mu_mbox_ops,
};