File: sunxi-msgbox.c

package info (click to toggle)
crust-firmware 0.6-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,900 kB
  • sloc: ansic: 19,341; yacc: 596; lex: 479; makefile: 334; asm: 215; sh: 136; python: 42
file content (143 lines) | stat: -rw-r--r-- 3,696 bytes parent folder | download | duplicates (2)
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
/*
 * Copyright © 2017-2022 The Crust Firmware Authors.
 * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only
 */

#include <debug.h>
#include <error.h>
#include <mmio.h>
#include <stdbool.h>
#include <stddef.h>
#include <util.h>
#include <clock/ccu.h>
#include <msgbox/sunxi-msgbox.h>
#include <platform/devices.h>

#include "msgbox.h"

#define CTRL_REG0           0x0000
#define CTRL_REG1           0x0004
#define CTRL_NORMAL         0x01100110

#define IRQ_EN_REG          0x0040
#define IRQ_STAT_REG        0x0050
#define REMOTE_IRQ_EN_REG   0x0060
#define REMOTE_IRQ_STAT_REG 0x0070
#define RX_IRQ(n)           BIT(0 + 2 * (n))
#define RX_IRQ_MASK         0x5555
#define TX_IRQ(n)           BIT(1 + 2 * (n))
#define TX_IRQ_MASK         0xaaaa

#define FIFO_STAT_REG(n)    (0x0100 + 0x4 * (n))
#define FIFO_STAT_MASK      BIT(0)

#define MSG_STAT_REG(n)     (0x0140 + 0x4 * (n))
#define MSG_STAT_MASK       GENMASK(2, 0)

#define MSG_DATA_REG(n)     (0x0180 + 0x4 * (n))

static bool
sunxi_msgbox_peek_data(const struct device *dev, uint8_t chan)
{
	const struct simple_device *self = to_simple_device(dev);

	return mmio_read_32(self->regs + MSG_STAT_REG(chan)) & MSG_STAT_MASK;
}

static void
sunxi_msgbox_ack_rx(const struct device *dev, uint8_t chan)
{
	const struct simple_device *self = to_simple_device(dev);

	mmio_write_32(self->regs + IRQ_STAT_REG, RX_IRQ(chan));
}

static bool
sunxi_msgbox_last_tx_done(const struct device *dev, uint8_t chan)
{
	const struct simple_device *self = to_simple_device(dev);

	assert(chan < SUNXI_MSGBOX_CHANS);

	return !(mmio_read_32(self->regs + REMOTE_IRQ_STAT_REG) &
	         RX_IRQ(chan));
}

static int
sunxi_msgbox_receive(const struct device *dev, uint8_t chan, uint32_t *msg)
{
	const struct simple_device *self = to_simple_device(dev);

	assert(chan < SUNXI_MSGBOX_CHANS);

	/* Check if a new message is available before reading it. */
	if (!sunxi_msgbox_peek_data(dev, chan))
		return ENOENT;
	*msg = mmio_read_32(self->regs + MSG_DATA_REG(chan));

	return SUCCESS;
}

static int
sunxi_msgbox_send(const struct device *dev, uint8_t chan, uint32_t msg)
{
	const struct simple_device *self = to_simple_device(dev);

	assert(chan < SUNXI_MSGBOX_CHANS);

	/* Reject the message if the FIFO is full. */
	if (mmio_read_32(self->regs + FIFO_STAT_REG(chan)) & FIFO_STAT_MASK)
		return EBUSY;
	mmio_write_32(self->regs + MSG_DATA_REG(chan), msg);

	return SUCCESS;
}

static int
sunxi_msgbox_probe(const struct device *dev)
{
	const struct simple_device *self = to_simple_device(dev);
	int err;

	if ((err = simple_device_probe(dev)))
		return err;

	/* Set even channels ARM -> SCP and odd channels SCP -> ARM. */
	mmio_write_32(self->regs + CTRL_REG0, CTRL_NORMAL);
	mmio_write_32(self->regs + CTRL_REG1, CTRL_NORMAL);

	/* Drain messages in RX channels (required to clear IRQs). */
	for (uint8_t chan = 0; chan < SUNXI_MSGBOX_CHANS; chan += 2) {
		while (sunxi_msgbox_peek_data(dev, chan))
			mmio_read_32(self->regs + MSG_DATA_REG(chan));
	}

	/* Disable and clear all IRQ. */
	mmio_write_32(self->regs + IRQ_EN_REG, 0);
	mmio_write_32(self->regs + IRQ_STAT_REG, GENMASK(15, 0));

	return SUCCESS;
}

static const struct msgbox_driver sunxi_msgbox_driver = {
	.drv = {
		.probe   = sunxi_msgbox_probe,
		.release = simple_device_release,
	},
	.ops = {
		.ack_rx       = sunxi_msgbox_ack_rx,
		.last_tx_done = sunxi_msgbox_last_tx_done,
		.receive      = sunxi_msgbox_receive,
		.send         = sunxi_msgbox_send,
	},
};

const struct simple_device msgbox = {
	.dev = {
		.name  = "msgbox",
		.drv   = &sunxi_msgbox_driver.drv,
		.state = DEVICE_STATE_INIT,
	},
	.clock = { .dev = &ccu.dev, .id = CLK_BUS_MSGBOX },
	.regs  = DEV_MSGBOX,
};