blob: 461390925d2ceac578622efdea2dc63163757a60 [file] [log] [blame]
Keerthyb3ab56c2021-06-22 12:04:27 +05301// SPDX-License-Identifier: GPL-2.0
2/*
3 * PRU-ICSS platform driver for various TI SoCs
4 *
5 * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/
6 */
7
8#include <common.h>
9#include <dm.h>
10#include <dm/of_access.h>
11#include <errno.h>
12#include <clk.h>
13#include <reset.h>
14#include <regmap.h>
15#include <syscon.h>
16#include <asm/io.h>
17#include <power-domain.h>
18#include <linux/pruss_driver.h>
19#include <dm/device_compat.h>
20
21#define PRUSS_CFG_IEPCLK 0x30
22#define ICSSG_CFG_CORE_SYNC 0x3c
23
24#define ICSSG_TASK_MGR_OFFSET 0x2a000
25
26/* PRUSS_IEPCLK register bits */
27#define PRUSS_IEPCLK_IEP_OCP_CLK_EN BIT(0)
28
29/* ICSSG CORE_SYNC register bits */
30#define ICSSG_CORE_VBUSP_SYNC_EN BIT(0)
31
32/*
33 * pruss_request_tm_region() - Request pruss for task manager region
34 * @dev: corresponding k3 device
35 * @loc: the task manager physical address
36 *
37 * Return: 0 if all goes good, else appropriate error message.
38 */
39int pruss_request_tm_region(struct udevice *dev, phys_addr_t *loc)
40{
41 struct pruss *priv;
42
43 priv = dev_get_priv(dev);
44 if (!priv || !priv->mem_regions[PRUSS_MEM_DRAM0].pa)
45 return -EINVAL;
46
47 *loc = priv->mem_regions[PRUSS_MEM_DRAM0].pa + ICSSG_TASK_MGR_OFFSET;
48
49 return 0;
50}
51
52/**
53 * pruss_request_mem_region() - request a memory resource
54 * @dev: the pruss device
55 * @mem_id: the memory resource id
56 * @region: pointer to memory region structure to be filled in
57 *
58 * This function allows a client driver to request a memory resource,
59 * and if successful, will let the client driver own the particular
60 * memory region until released using the pruss_release_mem_region()
61 * API.
62 *
63 * Returns the memory region if requested resource is available, an
64 * error otherwise
65 */
66int pruss_request_mem_region(struct udevice *dev, enum pruss_mem mem_id,
67 struct pruss_mem_region *region)
68{
69 struct pruss *pruss;
70
71 pruss = dev_get_priv(dev);
72 if (!pruss || !region)
73 return -EINVAL;
74
75 if (mem_id >= PRUSS_MEM_MAX)
76 return -EINVAL;
77
78 if (pruss->mem_in_use[mem_id])
79 return -EBUSY;
80
81 *region = pruss->mem_regions[mem_id];
82 pruss->mem_in_use[mem_id] = region;
83
84 return 0;
85}
86
87/**
88 * pruss_release_mem_region() - release a memory resource
89 * @dev: the pruss device
90 * @region: the memory region to release
91 *
92 * This function is the complimentary function to
93 * pruss_request_mem_region(), and allows the client drivers to
94 * release back a memory resource.
95 *
96 * Returns 0 on success, an error code otherwise
97 */
98int pruss_release_mem_region(struct udevice *dev,
99 struct pruss_mem_region *region)
100{
101 struct pruss *pruss;
102 int id;
103
104 pruss = dev_get_priv(dev);
105 if (!pruss || !region)
106 return -EINVAL;
107
108 /* find out the memory region being released */
109 for (id = 0; id < PRUSS_MEM_MAX; id++) {
110 if (pruss->mem_in_use[id] == region)
111 break;
112 }
113
114 if (id == PRUSS_MEM_MAX)
115 return -EINVAL;
116
117 pruss->mem_in_use[id] = NULL;
118
119 return 0;
120}
121
122/**
123 * pruss_cfg_update() - configure a PRUSS CFG sub-module register
124 * @dev: the pruss device
125 * @reg: register offset within the CFG sub-module
126 * @mask: bit mask to use for programming the @val
127 * @val: value to write
128 *
129 * Programs a given register within the PRUSS CFG sub-module
130 *
131 * Returns 0 on success, or an error code otherwise
132 */
133int pruss_cfg_update(struct udevice *dev, unsigned int reg,
134 unsigned int mask, unsigned int val)
135{
136 struct pruss *pruss;
137
138 pruss = dev_get_priv(dev);
139 if (IS_ERR_OR_NULL(pruss))
140 return -EINVAL;
141
142 return regmap_update_bits(pruss->cfg, reg, mask, val);
143}
144
145/**
146 * pruss_probe() - Basic probe
147 * @dev: corresponding k3 device
148 *
149 * Return: 0 if all goes good, else appropriate error message.
150 */
151static int pruss_probe(struct udevice *dev)
152{
153 const char *mem_names[PRUSS_MEM_MAX] = { "dram0", "dram1", "shrdram2" };
154 ofnode sub_node, node, memories;
155 struct udevice *syscon;
156 struct pruss *priv;
157 int ret, idx, i;
158
159 priv = dev_get_priv(dev);
160 node = dev_ofnode(dev);
161 priv->dev = dev;
162 memories = ofnode_find_subnode(node, "memories");
163
164 for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
165 idx = ofnode_stringlist_search(memories, "reg-names", mem_names[i]);
166 priv->mem_regions[i].pa = ofnode_get_addr_size_index(memories, idx,
167 (u64 *)&priv->mem_regions[i].size);
168 }
169
170 sub_node = ofnode_find_subnode(node, "cfg");
171 ret = uclass_get_device_by_ofnode(UCLASS_SYSCON, sub_node,
172 &syscon);
173
174 priv->cfg = syscon_get_regmap(syscon);
175 if (IS_ERR(priv->cfg)) {
176 dev_err(dev, "unable to get cfg regmap (%ld)\n",
177 PTR_ERR(priv->cfg));
178 return -ENODEV;
179 }
180
181 /*
182 * ToDo: To be modelled as clocks.
183 * The CORE block uses two multiplexers to allow software to
184 * select one of three source clocks (ICSSGn_CORE_CLK, ICSSGn_ICLK or
185 * ICSSGn_IEP_CLK) for the final clock source of the CORE block.
186 * The user needs to configure ICSSG_CORE_SYNC_REG[0] CORE_VBUSP_SYNC_EN
187 * bit & ICSSG_IEPCLK_REG[0] IEP_OCP_CLK_EN bit in order to select the
188 * clock source to the CORE block.
189 */
190 ret = regmap_update_bits(priv->cfg, ICSSG_CFG_CORE_SYNC,
191 ICSSG_CORE_VBUSP_SYNC_EN,
192 ICSSG_CORE_VBUSP_SYNC_EN);
193 if (ret)
194 return ret;
195 ret = regmap_update_bits(priv->cfg, PRUSS_CFG_IEPCLK,
196 PRUSS_IEPCLK_IEP_OCP_CLK_EN,
197 PRUSS_IEPCLK_IEP_OCP_CLK_EN);
198 if (ret)
199 return ret;
200
201 dev_dbg(dev, "pruss successfully probed %s\n", dev->name);
202
203 return 0;
204}
205
206static const struct udevice_id pruss_ids[] = {
207 { .compatible = "ti,am654-icssg"},
208 {}
209};
210
211U_BOOT_DRIVER(pruss) = {
212 .name = "pruss",
213 .of_match = pruss_ids,
214 .id = UCLASS_MISC,
215 .probe = pruss_probe,
216 .priv_auto = sizeof(struct pruss),
217};