1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Microchip's PolarFire SoC (MPFS) Mailbox Driver
4 *
5 * Copyright (C) 2024 Microchip Technology Inc. All rights reserved.
6 *
7 * Author: Jamie Gibbons <jamie.gibbons@microchip.com>
8 *
9 */
10
11 #include <asm/io.h>
12 #include <dm.h>
13 #include <dm/device-internal.h>
14 #include <dm/device.h>
15 #include <dm/device_compat.h>
16 #include <dm/devres.h>
17 #include <dm/ofnode.h>
18 #include <linux/bitops.h>
19 #include <linux/compat.h>
20 #include <linux/io.h>
21 #include <linux/ioport.h>
22 #include <log.h>
23 #include <mailbox-uclass.h>
24 #include <malloc.h>
25 #include <mpfs-mailbox.h>
26
27 #define SERVICES_CR_OFFSET 0x50u
28 #define SERVICES_SR_OFFSET 0x54u
29
30 #define SERVICE_CR_REQ_MASK 0x1u
31 #define SERVICE_SR_BUSY_MASK 0x2u
32 #define SERVICE_SR_STATUS_SHIFT 16
33 #define SERVICE_CR_COMMAND_SHIFT 16
34 #define MASK_8BIT 0xFF
35
36 struct mpfs_mbox {
37 struct udevice *dev;
38 void __iomem *ctrl_base;
39 void __iomem *mbox_base;
40 struct mbox_chan *chan;
41 };
42
mpfs_mbox_busy(struct mbox_chan * chan)43 static bool mpfs_mbox_busy(struct mbox_chan *chan)
44 {
45 struct mpfs_mbox *mbox = dev_get_priv(chan->dev);
46 uint16_t status;
47
48 status = readl(mbox->ctrl_base + SERVICES_SR_OFFSET);
49
50 return status & SERVICE_SR_BUSY_MASK;
51 }
52
mpfs_mbox_send(struct mbox_chan * chan,const void * data)53 static int mpfs_mbox_send(struct mbox_chan *chan, const void *data)
54 {
55 struct mpfs_mbox *mbox = dev_get_priv(chan->dev);
56 struct mpfs_mss_msg *msg = (struct mpfs_mss_msg *)data;
57 u32 mailbox_val, cmd_shifted, value;
58 u8 *byte_buf;
59 u8 idx, byte_idx, byte_offset;
60
61 u32 *word_buf = (u32 *)msg->cmd_data;
62
63 if (mpfs_mbox_busy(chan))
64 return -EBUSY;
65
66 for (idx = 0; idx < (msg->cmd_data_size / BYTES_4); idx++)
67 writel(word_buf[idx], mbox->mbox_base + msg->mbox_offset + idx * BYTES_4);
68
69 if ((msg->cmd_data_size % BYTES_4) > 0) {
70 byte_offset = (msg->cmd_data_size / BYTES_4) * BYTES_4;
71 byte_buf = (u8 *)(msg->cmd_data + byte_offset);
72 mailbox_val = readl(mbox->mbox_base + msg->mbox_offset + idx * BYTES_4);
73
74 for (byte_idx = 0; byte_idx < (msg->cmd_data_size % BYTES_4); byte_idx++) {
75 mailbox_val &= ~(MASK_8BIT << (byte_idx * 0x8u));
76 mailbox_val |= (u32)byte_buf[byte_idx] << (byte_idx * 0x8u);
77 }
78 writel(mailbox_val, mbox->mbox_base + msg->mbox_offset + idx * BYTES_4);
79 }
80
81 cmd_shifted = msg->cmd_opcode << SERVICE_CR_COMMAND_SHIFT;
82 cmd_shifted |= SERVICE_CR_REQ_MASK;
83 writel(cmd_shifted, mbox->ctrl_base + SERVICES_CR_OFFSET);
84
85 do {
86 value = readl(mbox->ctrl_base + SERVICES_CR_OFFSET);
87 } while (SERVICE_CR_REQ_MASK == (value & SERVICE_CR_REQ_MASK));
88
89 do {
90 value = readl(mbox->ctrl_base + SERVICES_SR_OFFSET);
91 } while (SERVICE_SR_BUSY_MASK == (value & SERVICE_SR_BUSY_MASK));
92
93 msg->response->resp_status = (value >> SERVICE_SR_STATUS_SHIFT);
94 if (msg->response->resp_status)
95 return -EBADMSG;
96
97 return 0;
98 }
99
mpfs_mbox_recv(struct mbox_chan * chan,void * data)100 static int mpfs_mbox_recv(struct mbox_chan *chan, void *data)
101 {
102 struct mpfs_mbox *mbox = dev_get_priv(chan->dev);
103 struct mpfs_mss_msg *msg = data;
104 struct mpfs_mss_response *response = msg->response;
105 u8 idx;
106
107 if (!response->resp_msg) {
108 dev_err(chan->dev, "failed to assign memory for response %d\n", -ENOMEM);
109 return -EINVAL;
110 }
111
112 if (mpfs_mbox_busy(chan)) {
113 dev_err(chan->dev, "mailbox is busy\n");
114 response->resp_status = 0xDEAD;
115 return -EINVAL;
116 }
117
118 for (idx = 0; idx < response->resp_size; idx++)
119 *((u8 *)(response->resp_msg) + idx) = readb(mbox->mbox_base + msg->resp_offset + idx);
120
121 return 0;
122 }
123
124 static const struct mbox_ops mpfs_mbox_ops = {
125 .send = mpfs_mbox_send,
126 .recv = mpfs_mbox_recv,
127 };
128
mpfs_mbox_probe(struct udevice * dev)129 static int mpfs_mbox_probe(struct udevice *dev)
130 {
131 struct mpfs_mbox *mbox;
132 struct resource regs;
133 ofnode node;
134 int ret;
135
136 node = dev_ofnode(dev);
137
138 mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
139 if (!mbox)
140 return -ENOMEM;
141
142 ret = ofnode_read_resource(node, 0, ®s);
143 if (ret) {
144 dev_err(dev, "No reg property for controller base\n");
145 return ret;
146 };
147
148 mbox->ctrl_base = devm_ioremap(dev, regs.start, regs.start - regs.end);
149
150 ret = ofnode_read_resource(node, 2, ®s);
151 if (ret) {
152 dev_err(dev, "No reg property for mailbox base\n");
153 return ret;
154 };
155
156 mbox->mbox_base = devm_ioremap(dev, regs.start, regs.start - regs.end);
157
158 mbox->dev = dev;
159 dev_set_priv(dev, mbox);
160 mbox->chan->con_priv = mbox;
161
162 return 0;
163 }
164
165 static const struct udevice_id mpfs_mbox_ids[] = {
166 {.compatible = "microchip,mpfs-mailbox"},
167 { }
168 };
169
170 U_BOOT_DRIVER(mpfs_mbox) = {
171 .name = "mpfs-mbox",
172 .id = UCLASS_MAILBOX,
173 .of_match = mpfs_mbox_ids,
174 .probe = mpfs_mbox_probe,
175 .priv_auto = sizeof(struct mpfs_mbox),
176 .ops = &mpfs_mbox_ops,
177 };
178