1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2016, NVIDIA CORPORATION. 4 */ 5 6 #include <common.h> 7 #include <dm.h> 8 #include <dm/lists.h> 9 #include <dm/root.h> 10 #include <mailbox.h> 11 #include <misc.h> 12 #include <asm/arch-tegra/bpmp_abi.h> 13 #include <asm/arch-tegra/ivc.h> 14 15 #define BPMP_IVC_FRAME_COUNT 1 16 #define BPMP_IVC_FRAME_SIZE 128 17 18 #define BPMP_FLAG_DO_ACK BIT(0) 19 #define BPMP_FLAG_RING_DOORBELL BIT(1) 20 21 DECLARE_GLOBAL_DATA_PTR; 22 23 struct tegra186_bpmp { 24 struct mbox_chan mbox; 25 struct tegra_ivc ivc; 26 }; 27 28 static int tegra186_bpmp_call(struct udevice *dev, int mrq, void *tx_msg, 29 int tx_size, void *rx_msg, int rx_size) 30 { 31 struct tegra186_bpmp *priv = dev_get_priv(dev); 32 int ret, err; 33 void *ivc_frame; 34 struct mrq_request *req; 35 struct mrq_response *resp; 36 ulong start_time; 37 38 debug("%s(dev=%p, mrq=%u, tx_msg=%p, tx_size=%d, rx_msg=%p, rx_size=%d) (priv=%p)\n", 39 __func__, dev, mrq, tx_msg, tx_size, rx_msg, rx_size, priv); 40 41 if ((tx_size > BPMP_IVC_FRAME_SIZE) || (rx_size > BPMP_IVC_FRAME_SIZE)) 42 return -EINVAL; 43 44 ret = tegra_ivc_write_get_next_frame(&priv->ivc, &ivc_frame); 45 if (ret) { 46 pr_err("tegra_ivc_write_get_next_frame() failed: %d\n", ret); 47 return ret; 48 } 49 50 req = ivc_frame; 51 req->mrq = mrq; 52 req->flags = BPMP_FLAG_DO_ACK | BPMP_FLAG_RING_DOORBELL; 53 memcpy(req + 1, tx_msg, tx_size); 54 55 ret = tegra_ivc_write_advance(&priv->ivc); 56 if (ret) { 57 pr_err("tegra_ivc_write_advance() failed: %d\n", ret); 58 return ret; 59 } 60 61 start_time = timer_get_us(); 62 for (;;) { 63 ret = tegra_ivc_channel_notified(&priv->ivc); 64 if (ret) { 65 pr_err("tegra_ivc_channel_notified() failed: %d\n", ret); 66 return ret; 67 } 68 69 ret = tegra_ivc_read_get_next_frame(&priv->ivc, &ivc_frame); 70 if (!ret) 71 break; 72 73 /* Timeout 20ms; roughly 10x current max observed duration */ 74 if ((timer_get_us() - start_time) > 20 * 1000) { 75 pr_err("tegra_ivc_read_get_next_frame() timed out (%d)\n", 76 ret); 77 return -ETIMEDOUT; 78 } 79 } 80 81 resp = ivc_frame; 82 err = resp->err; 83 if (!err && rx_msg && rx_size) 84 memcpy(rx_msg, resp + 1, rx_size); 85 86 ret = tegra_ivc_read_advance(&priv->ivc); 87 if (ret) { 88 pr_err("tegra_ivc_write_advance() failed: %d\n", ret); 89 return ret; 90 } 91 92 if (err) { 93 pr_err("BPMP responded with error %d\n", err); 94 /* err isn't a U-Boot error code, so don't that */ 95 return -EIO; 96 } 97 98 return rx_size; 99 } 100 101 /** 102 * The BPMP exposes multiple different services. We create a sub-device for 103 * each separate type of service, since each device must be of the appropriate 104 * UCLASS. 105 */ 106 static int tegra186_bpmp_bind(struct udevice *dev) 107 { 108 int ret; 109 struct udevice *child; 110 111 debug("%s(dev=%p)\n", __func__, dev); 112 113 ret = device_bind_driver_to_node(dev, "tegra186_clk", "tegra186_clk", 114 dev_ofnode(dev), &child); 115 if (ret) 116 return ret; 117 118 ret = device_bind_driver_to_node(dev, "tegra186_reset", 119 "tegra186_reset", dev_ofnode(dev), 120 &child); 121 if (ret) 122 return ret; 123 124 ret = device_bind_driver_to_node(dev, "tegra186_power_domain", 125 "tegra186_power_domain", 126 dev_ofnode(dev), &child); 127 if (ret) 128 return ret; 129 130 ret = dm_scan_fdt_dev(dev); 131 if (ret) 132 return ret; 133 134 return 0; 135 } 136 137 static ulong tegra186_bpmp_get_shmem(struct udevice *dev, int index) 138 { 139 int ret; 140 struct fdtdec_phandle_args args; 141 fdt_addr_t reg; 142 143 ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev), 144 "shmem", NULL, 0, index, &args); 145 if (ret < 0) { 146 pr_err("fdtdec_parse_phandle_with_args() failed: %d\n", ret); 147 return ret; 148 } 149 150 reg = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, args.node, 151 "reg", 0, NULL, true); 152 if (reg == FDT_ADDR_T_NONE) { 153 pr_err("fdtdec_get_addr_size_auto_noparent() failed\n"); 154 return -ENODEV; 155 } 156 157 return reg; 158 } 159 160 static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc) 161 { 162 struct tegra186_bpmp *priv = 163 container_of(ivc, struct tegra186_bpmp, ivc); 164 int ret; 165 166 ret = mbox_send(&priv->mbox, NULL); 167 if (ret) 168 pr_err("mbox_send() failed: %d\n", ret); 169 } 170 171 static int tegra186_bpmp_probe(struct udevice *dev) 172 { 173 struct tegra186_bpmp *priv = dev_get_priv(dev); 174 int ret; 175 ulong tx_base, rx_base, start_time; 176 177 debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); 178 179 ret = mbox_get_by_index(dev, 0, &priv->mbox); 180 if (ret) { 181 pr_err("mbox_get_by_index() failed: %d\n", ret); 182 return ret; 183 } 184 185 tx_base = tegra186_bpmp_get_shmem(dev, 0); 186 if (IS_ERR_VALUE(tx_base)) { 187 pr_err("tegra186_bpmp_get_shmem failed for tx_base\n"); 188 return tx_base; 189 } 190 rx_base = tegra186_bpmp_get_shmem(dev, 1); 191 if (IS_ERR_VALUE(rx_base)) { 192 pr_err("tegra186_bpmp_get_shmem failed for rx_base\n"); 193 return rx_base; 194 } 195 debug("shmem: rx=%lx, tx=%lx\n", rx_base, tx_base); 196 197 ret = tegra_ivc_init(&priv->ivc, rx_base, tx_base, BPMP_IVC_FRAME_COUNT, 198 BPMP_IVC_FRAME_SIZE, tegra186_bpmp_ivc_notify); 199 if (ret) { 200 pr_err("tegra_ivc_init() failed: %d\n", ret); 201 return ret; 202 } 203 204 tegra_ivc_channel_reset(&priv->ivc); 205 start_time = timer_get_us(); 206 for (;;) { 207 ret = tegra_ivc_channel_notified(&priv->ivc); 208 if (!ret) 209 break; 210 211 /* Timeout 100ms */ 212 if ((timer_get_us() - start_time) > 100 * 1000) { 213 pr_err("Initial IVC reset timed out (%d)\n", ret); 214 ret = -ETIMEDOUT; 215 goto err_free_mbox; 216 } 217 } 218 219 return 0; 220 221 err_free_mbox: 222 mbox_free(&priv->mbox); 223 224 return ret; 225 } 226 227 static int tegra186_bpmp_remove(struct udevice *dev) 228 { 229 struct tegra186_bpmp *priv = dev_get_priv(dev); 230 231 debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); 232 233 mbox_free(&priv->mbox); 234 235 return 0; 236 } 237 238 static struct misc_ops tegra186_bpmp_ops = { 239 .call = tegra186_bpmp_call, 240 }; 241 242 static const struct udevice_id tegra186_bpmp_ids[] = { 243 { .compatible = "nvidia,tegra186-bpmp" }, 244 { } 245 }; 246 247 U_BOOT_DRIVER(tegra186_bpmp) = { 248 .name = "tegra186_bpmp", 249 .id = UCLASS_MISC, 250 .of_match = tegra186_bpmp_ids, 251 .bind = tegra186_bpmp_bind, 252 .probe = tegra186_bpmp_probe, 253 .remove = tegra186_bpmp_remove, 254 .ops = &tegra186_bpmp_ops, 255 .priv_auto_alloc_size = sizeof(struct tegra186_bpmp), 256 }; 257