Loading...
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 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics 2019 - All Rights Reserved */ #define LOG_CATEGORY UCLASS_MAILBOX #include <clk.h> #include <dm.h> #include <log.h> #include <mailbox-uclass.h> #include <malloc.h> #include <asm/io.h> #include <dm/device_compat.h> #include <linux/bitops.h> /* * IPCC has one set of registers per CPU * IPCC_PROC_OFFST allows to define cpu registers set base address * according to the assigned proc_id. */ #define IPCC_PROC_OFFST 0x010 #define IPCC_XSCR 0x008 #define IPCC_XTOYSR 0x00c #define IPCC_HWCFGR 0x3f0 #define IPCFGR_CHAN_MASK GENMASK(7, 0) #define RX_BIT_CHAN(chan) BIT(chan) #define TX_BIT_SHIFT 16 #define TX_BIT_CHAN(chan) BIT(TX_BIT_SHIFT + (chan)) #define STM32_MAX_PROCS 2 struct stm32_ipcc { void __iomem *reg_base; void __iomem *reg_proc; u32 proc_id; u32 n_chans; }; static int stm32_ipcc_request(struct mbox_chan *chan) { struct stm32_ipcc *ipcc = dev_get_priv(chan->dev); dev_dbg(chan->dev, "chan=%p\n", chan); if (chan->id >= ipcc->n_chans) { dev_dbg(chan->dev, "failed to request channel: %ld\n", chan->id); return -EINVAL; } return 0; } static int stm32_ipcc_free(struct mbox_chan *chan) { dev_dbg(chan->dev, "chan=%p\n", chan); return 0; } static int stm32_ipcc_send(struct mbox_chan *chan, const void *data) { struct stm32_ipcc *ipcc = dev_get_priv(chan->dev); dev_dbg(chan->dev, "chan=%p, data=%p\n", chan, data); if (readl(ipcc->reg_proc + IPCC_XTOYSR) & BIT(chan->id)) return -EBUSY; /* set channel n occupied */ setbits_le32(ipcc->reg_proc + IPCC_XSCR, TX_BIT_CHAN(chan->id)); return 0; } static int stm32_ipcc_recv(struct mbox_chan *chan, void *data) { struct stm32_ipcc *ipcc = dev_get_priv(chan->dev); u32 val; int proc_offset; dev_dbg(chan->dev, "chan=%p, data=%p\n", chan, data); /* read 'channel occupied' status from other proc */ proc_offset = ipcc->proc_id ? -IPCC_PROC_OFFST : IPCC_PROC_OFFST; val = readl(ipcc->reg_proc + proc_offset + IPCC_XTOYSR); if (!(val & BIT(chan->id))) return -ENODATA; setbits_le32(ipcc->reg_proc + IPCC_XSCR, RX_BIT_CHAN(chan->id)); return 0; } static int stm32_ipcc_probe(struct udevice *dev) { struct stm32_ipcc *ipcc = dev_get_priv(dev); fdt_addr_t addr; struct clk clk; int ret; dev_dbg(dev, "\n"); addr = dev_read_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; ipcc->reg_base = (void __iomem *)addr; /* proc_id */ ret = dev_read_u32_index(dev, "st,proc_id", 1, &ipcc->proc_id); if (ret) { dev_dbg(dev, "Missing st,proc_id\n"); return -EINVAL; } if (ipcc->proc_id >= STM32_MAX_PROCS) { dev_err(dev, "Invalid proc_id (%d)\n", ipcc->proc_id); return -EINVAL; } ipcc->reg_proc = ipcc->reg_base + ipcc->proc_id * IPCC_PROC_OFFST; ret = clk_get_by_index(dev, 0, &clk); if (ret) return ret; ret = clk_enable(&clk); if (ret) return ret; /* get channel number */ ipcc->n_chans = readl(ipcc->reg_base + IPCC_HWCFGR); ipcc->n_chans &= IPCFGR_CHAN_MASK; return 0; } static const struct udevice_id stm32_ipcc_ids[] = { { .compatible = "st,stm32mp1-ipcc" }, { } }; struct mbox_ops stm32_ipcc_mbox_ops = { .request = stm32_ipcc_request, .rfree = stm32_ipcc_free, .send = stm32_ipcc_send, .recv = stm32_ipcc_recv, }; U_BOOT_DRIVER(stm32_ipcc) = { .name = "stm32_ipcc", .id = UCLASS_MAILBOX, .of_match = stm32_ipcc_ids, .probe = stm32_ipcc_probe, .priv_auto = sizeof(struct stm32_ipcc), .ops = &stm32_ipcc_mbox_ops, }; |