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