1 /* $NetBSD: sunxi_ccu.h,v 1.24 2024/09/16 23:37:13 macallan Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #ifndef _ARM_SUNXI_CCU_H 30 #define _ARM_SUNXI_CCU_H 31 32 #include <dev/clk/clk_backend.h> 33 34 struct sunxi_ccu_softc; 35 struct sunxi_ccu_clk; 36 struct sunxi_ccu_reset; 37 38 /* 39 * Resets 40 */ 41 42 struct sunxi_ccu_reset { 43 bus_size_t reg; 44 uint32_t mask; 45 }; 46 47 #define SUNXI_CCU_RESET(_id, _reg, _bit) \ 48 [_id] = { \ 49 .reg = (_reg), \ 50 .mask = __BIT(_bit), \ 51 } 52 53 /* 54 * Clocks 55 */ 56 57 enum sunxi_ccu_clktype { 58 SUNXI_CCU_UNKNOWN, 59 SUNXI_CCU_GATE, 60 SUNXI_CCU_NM, 61 SUNXI_CCU_NKMP, 62 SUNXI_CCU_PREDIV, 63 SUNXI_CCU_DIV, 64 SUNXI_CCU_PHASE, 65 SUNXI_CCU_FIXED_FACTOR, 66 SUNXI_CCU_FRACTIONAL, 67 SUNXI_CCU_MUX, 68 }; 69 70 struct sunxi_ccu_gate { 71 bus_size_t reg; 72 uint32_t mask; 73 const char *parent; 74 }; 75 76 int sunxi_ccu_gate_enable(struct sunxi_ccu_softc *, 77 struct sunxi_ccu_clk *, int); 78 const char *sunxi_ccu_gate_get_parent(struct sunxi_ccu_softc *, 79 struct sunxi_ccu_clk *); 80 81 #define SUNXI_CCU_GATE(_id, _name, _pname, _reg, _bit) \ 82 [_id] = { \ 83 .type = SUNXI_CCU_GATE, \ 84 .base.name = (_name), \ 85 .base.flags = CLK_SET_RATE_PARENT, \ 86 .u.gate.parent = (_pname), \ 87 .u.gate.reg = (_reg), \ 88 .u.gate.mask = __BIT(_bit), \ 89 .enable = sunxi_ccu_gate_enable, \ 90 .get_parent = sunxi_ccu_gate_get_parent, \ 91 } 92 93 struct sunxi_ccu_nkmp_tbl { 94 u_int rate; 95 uint32_t n; 96 uint32_t k; 97 uint32_t m; 98 uint32_t p; 99 }; 100 101 struct sunxi_ccu_nkmp { 102 bus_size_t reg; 103 const char *parent; 104 uint32_t n; 105 uint32_t k; 106 uint32_t m; 107 uint32_t p; 108 uint32_t lock; 109 uint32_t enable; 110 uint32_t flags; 111 const struct sunxi_ccu_nkmp_tbl *table; 112 #define SUNXI_CCU_NKMP_DIVIDE_BY_TWO __BIT(0) 113 #define SUNXI_CCU_NKMP_FACTOR_N_EXACT __BIT(1) 114 #define SUNXI_CCU_NKMP_SCALE_CLOCK __BIT(2) 115 #define SUNXI_CCU_NKMP_FACTOR_P_POW2 __BIT(3) 116 #define SUNXI_CCU_NKMP_FACTOR_N_ZERO_IS_ONE __BIT(4) 117 #define SUNXI_CCU_NKMP_FACTOR_P_X4 __BIT(5) 118 }; 119 120 int sunxi_ccu_nkmp_enable(struct sunxi_ccu_softc *, 121 struct sunxi_ccu_clk *, int); 122 u_int sunxi_ccu_nkmp_get_rate(struct sunxi_ccu_softc *, 123 struct sunxi_ccu_clk *); 124 int sunxi_ccu_nkmp_set_rate(struct sunxi_ccu_softc *, 125 struct sunxi_ccu_clk *, u_int); 126 const char *sunxi_ccu_nkmp_get_parent(struct sunxi_ccu_softc *, 127 struct sunxi_ccu_clk *); 128 129 #define SUNXI_CCU_NKMP_TABLE(_id, _name, _parent, _reg, _n, _k, _m, \ 130 _p, _enable, _lock, _tbl, _flags) \ 131 [_id] = { \ 132 .type = SUNXI_CCU_NKMP, \ 133 .base.name = (_name), \ 134 .u.nkmp.reg = (_reg), \ 135 .u.nkmp.parent = (_parent), \ 136 .u.nkmp.n = (_n), \ 137 .u.nkmp.k = (_k), \ 138 .u.nkmp.m = (_m), \ 139 .u.nkmp.p = (_p), \ 140 .u.nkmp.enable = (_enable), \ 141 .u.nkmp.flags = (_flags), \ 142 .u.nkmp.lock = (_lock), \ 143 .u.nkmp.table = (_tbl), \ 144 .enable = sunxi_ccu_nkmp_enable, \ 145 .get_rate = sunxi_ccu_nkmp_get_rate, \ 146 .set_rate = sunxi_ccu_nkmp_set_rate, \ 147 .get_parent = sunxi_ccu_nkmp_get_parent, \ 148 } 149 150 #define SUNXI_CCU_NKMP(_id, _name, _parent, _reg, _n, _k, _m, \ 151 _p, _enable, _flags) \ 152 SUNXI_CCU_NKMP_TABLE(_id, _name, _parent, _reg, _n, _k, _m, \ 153 _p, _enable, 0, NULL, _flags) 154 155 156 struct sunxi_ccu_nm { 157 bus_size_t reg; 158 const char **parents; 159 u_int nparents; 160 uint32_t n; 161 uint32_t m; 162 uint32_t sel; 163 uint32_t enable; 164 uint32_t flags; 165 #define SUNXI_CCU_NM_POWER_OF_TWO __BIT(0) 166 #define SUNXI_CCU_NM_ROUND_DOWN __BIT(1) 167 #define SUNXI_CCU_NM_DIVIDE_BY_TWO __BIT(2) 168 }; 169 170 int sunxi_ccu_nm_enable(struct sunxi_ccu_softc *, 171 struct sunxi_ccu_clk *, int); 172 u_int sunxi_ccu_nm_get_rate(struct sunxi_ccu_softc *, 173 struct sunxi_ccu_clk *); 174 int sunxi_ccu_nm_set_rate(struct sunxi_ccu_softc *, 175 struct sunxi_ccu_clk *, u_int); 176 int sunxi_ccu_nm_set_parent(struct sunxi_ccu_softc *, 177 struct sunxi_ccu_clk *, 178 const char *); 179 const char *sunxi_ccu_nm_get_parent(struct sunxi_ccu_softc *, 180 struct sunxi_ccu_clk *); 181 182 #define SUNXI_CCU_NM(_id, _name, _parents, _reg, _n, _m, _sel, \ 183 _enable, _flags) \ 184 [_id] = { \ 185 .type = SUNXI_CCU_NM, \ 186 .base.name = (_name), \ 187 .u.nm.reg = (_reg), \ 188 .u.nm.parents = (_parents), \ 189 .u.nm.nparents = __arraycount(_parents), \ 190 .u.nm.n = (_n), \ 191 .u.nm.m = (_m), \ 192 .u.nm.sel = (_sel), \ 193 .u.nm.enable = (_enable), \ 194 .u.nm.flags = (_flags), \ 195 .enable = sunxi_ccu_nm_enable, \ 196 .get_rate = sunxi_ccu_nm_get_rate, \ 197 .set_rate = sunxi_ccu_nm_set_rate, \ 198 .set_parent = sunxi_ccu_nm_set_parent, \ 199 .get_parent = sunxi_ccu_nm_get_parent, \ 200 } 201 202 struct sunxi_ccu_div { 203 bus_size_t reg; 204 const char **parents; 205 u_int nparents; 206 uint32_t div; 207 uint32_t sel; 208 uint32_t enable; 209 uint32_t flags; 210 #define SUNXI_CCU_DIV_POWER_OF_TWO __BIT(0) 211 #define SUNXI_CCU_DIV_ZERO_IS_ONE __BIT(1) 212 #define SUNXI_CCU_DIV_TIMES_TWO __BIT(2) 213 #define SUNXI_CCU_DIV_SET_RATE_PARENT __BIT(3) 214 }; 215 216 int sunxi_ccu_div_enable(struct sunxi_ccu_softc *, 217 struct sunxi_ccu_clk *, int); 218 u_int sunxi_ccu_div_get_rate(struct sunxi_ccu_softc *, 219 struct sunxi_ccu_clk *); 220 int sunxi_ccu_div_set_rate(struct sunxi_ccu_softc *, 221 struct sunxi_ccu_clk *, u_int); 222 int sunxi_ccu_div_set_parent(struct sunxi_ccu_softc *, 223 struct sunxi_ccu_clk *, 224 const char *); 225 const char *sunxi_ccu_div_get_parent(struct sunxi_ccu_softc *, 226 struct sunxi_ccu_clk *); 227 228 #define SUNXI_CCU_DIV(_id, _name, _parents, _reg, _div, \ 229 _sel, _flags) \ 230 SUNXI_CCU_DIV_GATE(_id, _name, _parents, _reg, _div, \ 231 _sel, 0, _flags) 232 233 #define SUNXI_CCU_DIV_GATE(_id, _name, _parents, _reg, _div, \ 234 _sel, _enable, _flags) \ 235 [_id] = { \ 236 .type = SUNXI_CCU_DIV, \ 237 .base.name = (_name), \ 238 .u.div.reg = (_reg), \ 239 .u.div.parents = (_parents), \ 240 .u.div.nparents = __arraycount(_parents), \ 241 .u.div.div = (_div), \ 242 .u.div.sel = (_sel), \ 243 .u.div.enable = (_enable), \ 244 .u.div.flags = (_flags), \ 245 .enable = sunxi_ccu_div_enable, \ 246 .get_rate = sunxi_ccu_div_get_rate, \ 247 .set_rate = sunxi_ccu_div_set_rate, \ 248 .set_parent = sunxi_ccu_div_set_parent, \ 249 .get_parent = sunxi_ccu_div_get_parent, \ 250 } 251 252 /* special case of the div model for display clocks */ 253 int sunxi_ccu_lcdxch0_set_rate(struct sunxi_ccu_softc *, 254 struct sunxi_ccu_clk *, struct sunxi_ccu_clk *, 255 struct sunxi_ccu_clk *, u_int); 256 u_int sunxi_ccu_lcdxch0_round_rate(struct sunxi_ccu_softc *, 257 struct sunxi_ccu_clk *, struct sunxi_ccu_clk *, 258 struct sunxi_ccu_clk *, u_int); 259 260 int sunxi_ccu_lcdxch1_set_rate(struct sunxi_ccu_softc *sc, 261 struct sunxi_ccu_clk *clk, struct sunxi_ccu_clk *pclk, 262 struct sunxi_ccu_clk *pclk_x2, u_int); 263 264 struct sunxi_ccu_prediv { 265 bus_size_t reg; 266 const char **parents; 267 u_int nparents; 268 uint32_t prediv; 269 uint32_t prediv_sel; 270 uint32_t prediv_fixed; 271 uint32_t div; 272 uint32_t sel; 273 uint32_t flags; 274 #define SUNXI_CCU_PREDIV_POWER_OF_TWO __BIT(0) 275 #define SUNXI_CCU_PREDIV_DIVIDE_BY_TWO __BIT(1) 276 }; 277 278 u_int sunxi_ccu_prediv_get_rate(struct sunxi_ccu_softc *, 279 struct sunxi_ccu_clk *); 280 int sunxi_ccu_prediv_set_rate(struct sunxi_ccu_softc *, 281 struct sunxi_ccu_clk *, u_int); 282 int sunxi_ccu_prediv_set_parent(struct sunxi_ccu_softc *, 283 struct sunxi_ccu_clk *, 284 const char *); 285 const char *sunxi_ccu_prediv_get_parent(struct sunxi_ccu_softc *, 286 struct sunxi_ccu_clk *); 287 288 #define SUNXI_CCU_PREDIV(_id, _name, _parents, _reg, _prediv, \ 289 _prediv_sel, _div, _sel, _flags) \ 290 SUNXI_CCU_PREDIV_FIXED(_id, _name, _parents, _reg, _prediv, \ 291 _prediv_sel, 0, _div, _sel, _flags) 292 293 #define SUNXI_CCU_PREDIV_FIXED(_id, _name, _parents, _reg, _prediv, \ 294 _prediv_sel, _prediv_fixed, _div, _sel, _flags) \ 295 [_id] = { \ 296 .type = SUNXI_CCU_PREDIV, \ 297 .base.name = (_name), \ 298 .u.prediv.reg = (_reg), \ 299 .u.prediv.parents = (_parents), \ 300 .u.prediv.nparents = __arraycount(_parents), \ 301 .u.prediv.prediv = (_prediv), \ 302 .u.prediv.prediv_sel = (_prediv_sel), \ 303 .u.prediv.prediv_fixed = (_prediv_fixed), \ 304 .u.prediv.div = (_div), \ 305 .u.prediv.sel = (_sel), \ 306 .u.prediv.flags = (_flags), \ 307 .get_rate = sunxi_ccu_prediv_get_rate, \ 308 .set_rate = sunxi_ccu_prediv_set_rate, \ 309 .set_parent = sunxi_ccu_prediv_set_parent, \ 310 .get_parent = sunxi_ccu_prediv_get_parent, \ 311 } 312 313 struct sunxi_ccu_phase { 314 bus_size_t reg; 315 const char *parent; 316 uint32_t mask; 317 }; 318 319 u_int sunxi_ccu_phase_get_rate(struct sunxi_ccu_softc *, 320 struct sunxi_ccu_clk *); 321 int sunxi_ccu_phase_set_rate(struct sunxi_ccu_softc *, 322 struct sunxi_ccu_clk *, u_int); 323 const char *sunxi_ccu_phase_get_parent(struct sunxi_ccu_softc *, 324 struct sunxi_ccu_clk *); 325 326 #define SUNXI_CCU_PHASE(_id, _name, _parent, _reg, _mask) \ 327 [_id] = { \ 328 .type = SUNXI_CCU_PHASE, \ 329 .base.name = (_name), \ 330 .u.phase.reg = (_reg), \ 331 .u.phase.parent = (_parent), \ 332 .u.phase.mask = (_mask), \ 333 .get_rate = sunxi_ccu_phase_get_rate, \ 334 .set_rate = sunxi_ccu_phase_set_rate, \ 335 .get_parent = sunxi_ccu_phase_get_parent, \ 336 } 337 338 struct sunxi_ccu_fixed_factor { 339 const char *parent; 340 u_int div; 341 u_int mult; 342 }; 343 344 u_int sunxi_ccu_fixed_factor_get_rate(struct sunxi_ccu_softc *, 345 struct sunxi_ccu_clk *); 346 int sunxi_ccu_fixed_factor_set_rate(struct sunxi_ccu_softc *, 347 struct sunxi_ccu_clk *, u_int); 348 const char *sunxi_ccu_fixed_factor_get_parent(struct sunxi_ccu_softc *, 349 struct sunxi_ccu_clk *); 350 351 #define SUNXI_CCU_FIXED_FACTOR(_id, _name, _parent, _div, _mult) \ 352 [_id] = { \ 353 .type = SUNXI_CCU_FIXED_FACTOR, \ 354 .base.name = (_name), \ 355 .u.fixed_factor.parent = (_parent), \ 356 .u.fixed_factor.div = (_div), \ 357 .u.fixed_factor.mult = (_mult), \ 358 .get_rate = sunxi_ccu_fixed_factor_get_rate, \ 359 .get_parent = sunxi_ccu_fixed_factor_get_parent, \ 360 .set_rate = sunxi_ccu_fixed_factor_set_rate, \ 361 } 362 363 struct sunxi_ccu_fractional { 364 bus_size_t reg; 365 const char *parent; 366 uint32_t m; 367 uint32_t m_min; 368 uint32_t m_max; 369 uint32_t div_en; 370 uint32_t frac_sel; 371 uint32_t frac[2]; 372 uint32_t prediv; 373 uint32_t prediv_val; 374 uint32_t enable; 375 uint32_t flags; 376 #define SUNXI_CCU_FRACTIONAL_PLUSONE __BIT(0) 377 #define SUNXI_CCU_FRACTIONAL_SET_ENABLE __BIT(1) 378 }; 379 380 int sunxi_ccu_fractional_enable(struct sunxi_ccu_softc *, 381 struct sunxi_ccu_clk *, int); 382 u_int sunxi_ccu_fractional_get_rate(struct sunxi_ccu_softc *, 383 struct sunxi_ccu_clk *); 384 int sunxi_ccu_fractional_set_rate(struct sunxi_ccu_softc *, 385 struct sunxi_ccu_clk *, u_int); 386 u_int sunxi_ccu_fractional_round_rate(struct sunxi_ccu_softc *, 387 struct sunxi_ccu_clk *, u_int); 388 const char *sunxi_ccu_fractional_get_parent(struct sunxi_ccu_softc *, 389 struct sunxi_ccu_clk *); 390 391 #define SUNXI_CCU_FRACTIONAL(_id, _name, _parent, _reg, _m, _m_min, _m_max, \ 392 _div_en, _frac_sel, _frac0, _frac1, _prediv, _prediv_val, \ 393 _enable, _flags) \ 394 [_id] = { \ 395 .type = SUNXI_CCU_FRACTIONAL, \ 396 .base.name = (_name), \ 397 .u.fractional.reg = (_reg), \ 398 .u.fractional.parent = (_parent), \ 399 .u.fractional.m = (_m), \ 400 .u.fractional.m_min = (_m_min), \ 401 .u.fractional.m_max = (_m_max), \ 402 .u.fractional.prediv = (_prediv), \ 403 .u.fractional.prediv_val = (_prediv_val), \ 404 .u.fractional.div_en = (_div_en), \ 405 .u.fractional.frac_sel = (_frac_sel), \ 406 .u.fractional.frac[0] = (_frac0), \ 407 .u.fractional.frac[1] = (_frac1), \ 408 .u.fractional.enable = (_enable), \ 409 .u.fractional.flags = (_flags), \ 410 .enable = sunxi_ccu_fractional_enable, \ 411 .get_rate = sunxi_ccu_fractional_get_rate, \ 412 .set_rate = sunxi_ccu_fractional_set_rate, \ 413 .round_rate = sunxi_ccu_fractional_round_rate, \ 414 .get_parent = sunxi_ccu_fractional_get_parent, \ 415 } 416 417 struct sunxi_ccu_mux { 418 bus_size_t reg; 419 const char **parents; 420 u_int nparents; 421 uint32_t sel; 422 uint32_t flags; 423 }; 424 425 int sunxi_ccu_mux_set_parent(struct sunxi_ccu_softc *, 426 struct sunxi_ccu_clk *, 427 const char *); 428 const char *sunxi_ccu_mux_get_parent(struct sunxi_ccu_softc *, 429 struct sunxi_ccu_clk *); 430 431 #define SUNXI_CCU_MUX(_id, _name, _parents, _reg, _sel, _flags) \ 432 [_id] = { \ 433 .type = SUNXI_CCU_MUX, \ 434 .base.name = (_name), \ 435 .base.flags = CLK_SET_RATE_PARENT, \ 436 .u.mux.reg = (_reg), \ 437 .u.mux.parents = (_parents), \ 438 .u.mux.nparents = __arraycount(_parents), \ 439 .u.mux.sel = (_sel), \ 440 .u.mux.flags = (_flags), \ 441 .set_parent = sunxi_ccu_mux_set_parent, \ 442 .get_parent = sunxi_ccu_mux_get_parent, \ 443 } 444 445 446 struct sunxi_ccu_clk { 447 struct clk base; 448 enum sunxi_ccu_clktype type; 449 union { 450 struct sunxi_ccu_gate gate; 451 struct sunxi_ccu_nm nm; 452 struct sunxi_ccu_nkmp nkmp; 453 struct sunxi_ccu_prediv prediv; 454 struct sunxi_ccu_div div; 455 struct sunxi_ccu_phase phase; 456 struct sunxi_ccu_fixed_factor fixed_factor; 457 struct sunxi_ccu_fractional fractional; 458 struct sunxi_ccu_mux mux; 459 } u; 460 461 int (*enable)(struct sunxi_ccu_softc *, 462 struct sunxi_ccu_clk *, int); 463 u_int (*get_rate)(struct sunxi_ccu_softc *, 464 struct sunxi_ccu_clk *); 465 int (*set_rate)(struct sunxi_ccu_softc *, 466 struct sunxi_ccu_clk *, u_int); 467 u_int (*round_rate)(struct sunxi_ccu_softc *, 468 struct sunxi_ccu_clk *, u_int); 469 const char * (*get_parent)(struct sunxi_ccu_softc *, 470 struct sunxi_ccu_clk *); 471 int (*set_parent)(struct sunxi_ccu_softc *, 472 struct sunxi_ccu_clk *, 473 const char *); 474 }; 475 476 struct sunxi_ccu_softc { 477 device_t sc_dev; 478 int sc_phandle; 479 bus_space_tag_t sc_bst; 480 bus_space_handle_t sc_bsh; 481 482 struct clk_domain sc_clkdom; 483 484 struct sunxi_ccu_reset *sc_resets; 485 u_int sc_nresets; 486 487 struct sunxi_ccu_clk *sc_clks; 488 u_int sc_nclks; 489 }; 490 491 int sunxi_ccu_attach(struct sunxi_ccu_softc *); 492 struct sunxi_ccu_clk *sunxi_ccu_clock_find(struct sunxi_ccu_softc *, 493 const char *); 494 void sunxi_ccu_print(struct sunxi_ccu_softc *); 495 496 #define CCU_READ(sc, reg) \ 497 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 498 #define CCU_WRITE(sc, reg, val) \ 499 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 500 501 #endif /* _ARM_SUNXI_CCU_H */ 502