Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 465 lines 12 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright 2022 NXP, Peng Fan <peng.fan@nxp.com> 4 */ 5 6#include <linux/clk.h> 7#include <linux/device.h> 8#include <linux/module.h> 9#include <linux/of.h> 10#include <linux/of_platform.h> 11#include <linux/platform_device.h> 12#include <linux/pm_domain.h> 13#include <linux/pm_runtime.h> 14#include <linux/regmap.h> 15#include <linux/sizes.h> 16 17#include <dt-bindings/power/fsl,imx93-power.h> 18 19#define BLK_SFT_RSTN 0x0 20#define BLK_CLK_EN 0x4 21#define BLK_MAX_CLKS 4 22 23#define DOMAIN_MAX_CLKS 4 24 25#define LCDIF_QOS_REG 0xC 26#define LCDIF_DEFAULT_QOS_OFF 12 27#define LCDIF_CFG_QOS_OFF 8 28 29#define PXP_QOS_REG 0x10 30#define PXP_R_DEFAULT_QOS_OFF 28 31#define PXP_R_CFG_QOS_OFF 24 32#define PXP_W_DEFAULT_QOS_OFF 20 33#define PXP_W_CFG_QOS_OFF 16 34 35#define ISI_CACHE_REG 0x14 36 37#define ISI_QOS_REG 0x1C 38#define ISI_V_DEFAULT_QOS_OFF 28 39#define ISI_V_CFG_QOS_OFF 24 40#define ISI_U_DEFAULT_QOS_OFF 20 41#define ISI_U_CFG_QOS_OFF 16 42#define ISI_Y_R_DEFAULT_QOS_OFF 12 43#define ISI_Y_R_CFG_QOS_OFF 8 44#define ISI_Y_W_DEFAULT_QOS_OFF 4 45#define ISI_Y_W_CFG_QOS_OFF 0 46 47#define PRIO_MASK 0xF 48 49#define PRIO(X) (X) 50 51struct imx93_blk_ctrl_domain; 52 53struct imx93_blk_ctrl { 54 struct device *dev; 55 struct regmap *regmap; 56 int num_clks; 57 struct clk_bulk_data clks[BLK_MAX_CLKS]; 58 struct imx93_blk_ctrl_domain *domains; 59 struct genpd_onecell_data onecell_data; 60}; 61 62#define DOMAIN_MAX_QOS 4 63 64struct imx93_blk_ctrl_qos { 65 u32 reg; 66 u32 cfg_off; 67 u32 default_prio; 68 u32 cfg_prio; 69}; 70 71struct imx93_blk_ctrl_domain_data { 72 const char *name; 73 const char * const *clk_names; 74 int num_clks; 75 u32 rst_mask; 76 u32 clk_mask; 77 int num_qos; 78 struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS]; 79}; 80 81struct imx93_blk_ctrl_domain { 82 struct generic_pm_domain genpd; 83 const struct imx93_blk_ctrl_domain_data *data; 84 struct clk_bulk_data clks[DOMAIN_MAX_CLKS]; 85 struct imx93_blk_ctrl *bc; 86}; 87 88struct imx93_blk_ctrl_data { 89 const struct imx93_blk_ctrl_domain_data *domains; 90 u32 skip_mask; 91 int num_domains; 92 const char * const *clk_names; 93 int num_clks; 94 const struct regmap_access_table *reg_access_table; 95}; 96 97static inline struct imx93_blk_ctrl_domain * 98to_imx93_blk_ctrl_domain(struct generic_pm_domain *genpd) 99{ 100 return container_of(genpd, struct imx93_blk_ctrl_domain, genpd); 101} 102 103static int imx93_blk_ctrl_set_qos(struct imx93_blk_ctrl_domain *domain) 104{ 105 const struct imx93_blk_ctrl_domain_data *data = domain->data; 106 struct imx93_blk_ctrl *bc = domain->bc; 107 const struct imx93_blk_ctrl_qos *qos; 108 u32 val, mask; 109 int i; 110 111 for (i = 0; i < data->num_qos; i++) { 112 qos = &data->qos[i]; 113 114 mask = PRIO_MASK << qos->cfg_off; 115 mask |= PRIO_MASK << (qos->cfg_off + 4); 116 val = qos->cfg_prio << qos->cfg_off; 117 val |= qos->default_prio << (qos->cfg_off + 4); 118 119 regmap_write_bits(bc->regmap, qos->reg, mask, val); 120 121 dev_dbg(bc->dev, "data->qos[i].reg 0x%x 0x%x\n", qos->reg, val); 122 } 123 124 return 0; 125} 126 127static int imx93_blk_ctrl_power_on(struct generic_pm_domain *genpd) 128{ 129 struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd); 130 const struct imx93_blk_ctrl_domain_data *data = domain->data; 131 struct imx93_blk_ctrl *bc = domain->bc; 132 int ret; 133 134 ret = clk_bulk_prepare_enable(bc->num_clks, bc->clks); 135 if (ret) { 136 dev_err(bc->dev, "failed to enable bus clocks\n"); 137 return ret; 138 } 139 140 ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); 141 if (ret) { 142 clk_bulk_disable_unprepare(bc->num_clks, bc->clks); 143 dev_err(bc->dev, "failed to enable clocks\n"); 144 return ret; 145 } 146 147 ret = pm_runtime_get_sync(bc->dev); 148 if (ret < 0) { 149 pm_runtime_put_noidle(bc->dev); 150 dev_err(bc->dev, "failed to power up domain\n"); 151 goto disable_clk; 152 } 153 154 /* ungate clk */ 155 regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); 156 157 /* release reset */ 158 regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); 159 160 dev_dbg(bc->dev, "pd_on: name: %s\n", genpd->name); 161 162 return imx93_blk_ctrl_set_qos(domain); 163 164disable_clk: 165 clk_bulk_disable_unprepare(data->num_clks, domain->clks); 166 167 clk_bulk_disable_unprepare(bc->num_clks, bc->clks); 168 169 return ret; 170} 171 172static int imx93_blk_ctrl_power_off(struct generic_pm_domain *genpd) 173{ 174 struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd); 175 const struct imx93_blk_ctrl_domain_data *data = domain->data; 176 struct imx93_blk_ctrl *bc = domain->bc; 177 178 dev_dbg(bc->dev, "pd_off: name: %s\n", genpd->name); 179 180 regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); 181 regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); 182 183 pm_runtime_put(bc->dev); 184 185 clk_bulk_disable_unprepare(data->num_clks, domain->clks); 186 187 clk_bulk_disable_unprepare(bc->num_clks, bc->clks); 188 189 return 0; 190} 191 192static void imx93_release_genpd_provider(void *data) 193{ 194 struct device_node *of_node = data; 195 196 of_genpd_del_provider(of_node); 197} 198 199static void imx93_release_pm_genpd(void *data) 200{ 201 struct generic_pm_domain *genpd = data; 202 203 pm_genpd_remove(genpd); 204} 205 206static struct lock_class_key blk_ctrl_genpd_lock_class; 207 208static int imx93_blk_ctrl_probe(struct platform_device *pdev) 209{ 210 struct device *dev = &pdev->dev; 211 const struct imx93_blk_ctrl_data *bc_data = of_device_get_match_data(dev); 212 struct imx93_blk_ctrl *bc; 213 void __iomem *base; 214 int i, ret; 215 216 struct regmap_config regmap_config = { 217 .reg_bits = 32, 218 .val_bits = 32, 219 .reg_stride = 4, 220 .rd_table = bc_data->reg_access_table, 221 .wr_table = bc_data->reg_access_table, 222 .max_register = SZ_4K, 223 }; 224 225 bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); 226 if (!bc) 227 return -ENOMEM; 228 229 bc->dev = dev; 230 231 base = devm_platform_ioremap_resource(pdev, 0); 232 if (IS_ERR(base)) 233 return PTR_ERR(base); 234 235 bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config); 236 if (IS_ERR(bc->regmap)) 237 return dev_err_probe(dev, PTR_ERR(bc->regmap), 238 "failed to init regmap\n"); 239 240 bc->domains = devm_kcalloc(dev, bc_data->num_domains, 241 sizeof(struct imx93_blk_ctrl_domain), 242 GFP_KERNEL); 243 if (!bc->domains) 244 return -ENOMEM; 245 246 bc->onecell_data.num_domains = bc_data->num_domains; 247 bc->onecell_data.domains = 248 devm_kcalloc(dev, bc_data->num_domains, 249 sizeof(struct generic_pm_domain *), GFP_KERNEL); 250 if (!bc->onecell_data.domains) 251 return -ENOMEM; 252 253 for (i = 0; i < bc_data->num_clks; i++) 254 bc->clks[i].id = bc_data->clk_names[i]; 255 bc->num_clks = bc_data->num_clks; 256 257 ret = devm_clk_bulk_get(dev, bc->num_clks, bc->clks); 258 if (ret) 259 return dev_err_probe(dev, ret, "failed to get bus clock\n"); 260 261 for (i = 0; i < bc_data->num_domains; i++) { 262 const struct imx93_blk_ctrl_domain_data *data = &bc_data->domains[i]; 263 struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; 264 int j; 265 266 domain->data = data; 267 if (bc_data->skip_mask & BIT(i)) 268 continue; 269 270 for (j = 0; j < data->num_clks; j++) 271 domain->clks[j].id = data->clk_names[j]; 272 273 ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); 274 if (ret) 275 return dev_err_probe(dev, ret, "failed to get clock\n"); 276 277 domain->genpd.name = data->name; 278 domain->genpd.power_on = imx93_blk_ctrl_power_on; 279 domain->genpd.power_off = imx93_blk_ctrl_power_off; 280 domain->bc = bc; 281 282 ret = pm_genpd_init(&domain->genpd, NULL, true); 283 if (ret) 284 return dev_err_probe(dev, ret, "failed to init power domain\n"); 285 286 ret = devm_add_action_or_reset(dev, imx93_release_pm_genpd, &domain->genpd); 287 if (ret) 288 return dev_err_probe(dev, ret, "failed to add pm_genpd release callback\n"); 289 /* 290 * We use runtime PM to trigger power on/off of the upstream GPC 291 * domain, as a strict hierarchical parent/child power domain 292 * setup doesn't allow us to meet the sequencing requirements. 293 * This means we have nested locking of genpd locks, without the 294 * nesting being visible at the genpd level, so we need a 295 * separate lock class to make lockdep aware of the fact that 296 * this are separate domain locks that can be nested without a 297 * self-deadlock. 298 */ 299 lockdep_set_class(&domain->genpd.mlock, 300 &blk_ctrl_genpd_lock_class); 301 302 bc->onecell_data.domains[i] = &domain->genpd; 303 } 304 305 ret = devm_pm_runtime_enable(dev); 306 if (ret) 307 return dev_err_probe(dev, ret, "failed to enable pm-runtime\n"); 308 309 ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); 310 if (ret) 311 return dev_err_probe(dev, ret, "failed to add power domain provider\n"); 312 313 ret = devm_add_action_or_reset(dev, imx93_release_genpd_provider, dev->of_node); 314 if (ret) 315 return dev_err_probe(dev, ret, "failed to add genpd_provider release callback\n"); 316 317 ret = devm_of_platform_populate(dev); 318 if (ret) 319 return dev_err_probe(dev, ret, "failed to populate blk-ctrl sub-devices\n"); 320 321 return 0; 322} 323 324static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] = { 325 [IMX93_MEDIABLK_PD_MIPI_DSI] = { 326 .name = "mediablk-mipi-dsi", 327 .clk_names = (const char *[]){ "dsi" }, 328 .num_clks = 1, 329 .rst_mask = BIT(11) | BIT(12), 330 .clk_mask = BIT(11) | BIT(12), 331 }, 332 [IMX93_MEDIABLK_PD_MIPI_CSI] = { 333 .name = "mediablk-mipi-csi", 334 .clk_names = (const char *[]){ "cam", "csi" }, 335 .num_clks = 2, 336 .rst_mask = BIT(9) | BIT(10), 337 .clk_mask = BIT(9) | BIT(10), 338 }, 339 [IMX93_MEDIABLK_PD_PXP] = { 340 .name = "mediablk-pxp", 341 .clk_names = (const char *[]){ "pxp" }, 342 .num_clks = 1, 343 .rst_mask = BIT(7) | BIT(8), 344 .clk_mask = BIT(7) | BIT(8), 345 .num_qos = 2, 346 .qos = { 347 { 348 .reg = PXP_QOS_REG, 349 .cfg_off = PXP_R_CFG_QOS_OFF, 350 .default_prio = PRIO(3), 351 .cfg_prio = PRIO(6), 352 }, { 353 .reg = PXP_QOS_REG, 354 .cfg_off = PXP_W_CFG_QOS_OFF, 355 .default_prio = PRIO(3), 356 .cfg_prio = PRIO(6), 357 } 358 } 359 }, 360 [IMX93_MEDIABLK_PD_LCDIF] = { 361 .name = "mediablk-lcdif", 362 .clk_names = (const char *[]){ "disp", "lcdif" }, 363 .num_clks = 2, 364 .rst_mask = BIT(4) | BIT(5) | BIT(6), 365 .clk_mask = BIT(4) | BIT(5) | BIT(6), 366 .num_qos = 1, 367 .qos = { 368 { 369 .reg = LCDIF_QOS_REG, 370 .cfg_off = LCDIF_CFG_QOS_OFF, 371 .default_prio = PRIO(3), 372 .cfg_prio = PRIO(7), 373 } 374 } 375 }, 376 [IMX93_MEDIABLK_PD_ISI] = { 377 .name = "mediablk-isi", 378 .clk_names = (const char *[]){ "isi" }, 379 .num_clks = 1, 380 .rst_mask = BIT(2) | BIT(3), 381 .clk_mask = BIT(2) | BIT(3), 382 .num_qos = 4, 383 .qos = { 384 { 385 .reg = ISI_QOS_REG, 386 .cfg_off = ISI_Y_W_CFG_QOS_OFF, 387 .default_prio = PRIO(3), 388 .cfg_prio = PRIO(7), 389 }, { 390 .reg = ISI_QOS_REG, 391 .cfg_off = ISI_Y_R_CFG_QOS_OFF, 392 .default_prio = PRIO(3), 393 .cfg_prio = PRIO(7), 394 }, { 395 .reg = ISI_QOS_REG, 396 .cfg_off = ISI_U_CFG_QOS_OFF, 397 .default_prio = PRIO(3), 398 .cfg_prio = PRIO(7), 399 }, { 400 .reg = ISI_QOS_REG, 401 .cfg_off = ISI_V_CFG_QOS_OFF, 402 .default_prio = PRIO(3), 403 .cfg_prio = PRIO(7), 404 } 405 } 406 }, 407}; 408 409static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = { 410 regmap_reg_range(BLK_SFT_RSTN, BLK_CLK_EN), 411 regmap_reg_range(LCDIF_QOS_REG, ISI_CACHE_REG), 412 regmap_reg_range(ISI_QOS_REG, ISI_QOS_REG), 413}; 414 415static const struct regmap_access_table imx93_media_blk_ctl_access_table = { 416 .yes_ranges = imx93_media_blk_ctl_yes_ranges, 417 .n_yes_ranges = ARRAY_SIZE(imx93_media_blk_ctl_yes_ranges), 418}; 419 420static const char * const media_blk_clk_names[] = { 421 "axi", "apb", "nic" 422}; 423 424static const struct imx93_blk_ctrl_data imx91_media_blk_ctl_dev_data = { 425 .domains = imx93_media_blk_ctl_domain_data, 426 .skip_mask = BIT(IMX93_MEDIABLK_PD_MIPI_DSI) | BIT(IMX93_MEDIABLK_PD_PXP), 427 .num_domains = ARRAY_SIZE(imx93_media_blk_ctl_domain_data), 428 .clk_names = media_blk_clk_names, 429 .num_clks = ARRAY_SIZE(media_blk_clk_names), 430 .reg_access_table = &imx93_media_blk_ctl_access_table, 431}; 432 433static const struct imx93_blk_ctrl_data imx93_media_blk_ctl_dev_data = { 434 .domains = imx93_media_blk_ctl_domain_data, 435 .num_domains = ARRAY_SIZE(imx93_media_blk_ctl_domain_data), 436 .clk_names = media_blk_clk_names, 437 .num_clks = ARRAY_SIZE(media_blk_clk_names), 438 .reg_access_table = &imx93_media_blk_ctl_access_table, 439}; 440 441static const struct of_device_id imx93_blk_ctrl_of_match[] = { 442 { 443 .compatible = "fsl,imx91-media-blk-ctrl", 444 .data = &imx91_media_blk_ctl_dev_data 445 }, { 446 .compatible = "fsl,imx93-media-blk-ctrl", 447 .data = &imx93_media_blk_ctl_dev_data 448 }, { 449 /* Sentinel */ 450 } 451}; 452MODULE_DEVICE_TABLE(of, imx93_blk_ctrl_of_match); 453 454static struct platform_driver imx93_blk_ctrl_driver = { 455 .probe = imx93_blk_ctrl_probe, 456 .driver = { 457 .name = "imx93-blk-ctrl", 458 .of_match_table = imx93_blk_ctrl_of_match, 459 }, 460}; 461module_platform_driver(imx93_blk_ctrl_driver); 462 463MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 464MODULE_DESCRIPTION("i.MX93 BLK CTRL driver"); 465MODULE_LICENSE("GPL");