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