linux/arch/arm/mach-omap2/cm_common.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * OMAP2+ common Clock Management (CM) IP block functions
   4 *
   5 * Copyright (C) 2012 Texas Instruments, Inc.
   6 * Paul Walmsley
   7 *
   8 * XXX This code should eventually be moved to a CM driver.
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/init.h>
  13#include <linux/errno.h>
  14#include <linux/bug.h>
  15#include <linux/of.h>
  16#include <linux/of_address.h>
  17
  18#include "cm2xxx.h"
  19#include "cm3xxx.h"
  20#include "cm33xx.h"
  21#include "cm44xx.h"
  22#include "clock.h"
  23
  24/*
  25 * cm_ll_data: function pointers to SoC-specific implementations of
  26 * common CM functions
  27 */
  28static struct cm_ll_data null_cm_ll_data;
  29static const struct cm_ll_data *cm_ll_data = &null_cm_ll_data;
  30
  31/* cm_base: base virtual address of the CM IP block */
  32struct omap_domain_base cm_base;
  33
  34/* cm2_base: base virtual address of the CM2 IP block (OMAP44xx only) */
  35struct omap_domain_base cm2_base;
  36
  37#define CM_NO_CLOCKS            0x1
  38#define CM_SINGLE_INSTANCE      0x2
  39
  40/**
  41 * cm_split_idlest_reg - split CM_IDLEST reg addr into its components
  42 * @idlest_reg: CM_IDLEST* virtual address
  43 * @prcm_inst: pointer to an s16 to return the PRCM instance offset
  44 * @idlest_reg_id: pointer to a u8 to return the CM_IDLESTx register ID
  45 *
  46 * Given an absolute CM_IDLEST register address @idlest_reg, passes
  47 * the PRCM instance offset and IDLEST register ID back to the caller
  48 * via the @prcm_inst and @idlest_reg_id.  Returns -EINVAL upon error,
  49 * or 0 upon success.  XXX This function is only needed until absolute
  50 * register addresses are removed from the OMAP struct clk records.
  51 */
  52int cm_split_idlest_reg(struct clk_omap_reg *idlest_reg, s16 *prcm_inst,
  53                        u8 *idlest_reg_id)
  54{
  55        int ret;
  56        if (!cm_ll_data->split_idlest_reg) {
  57                WARN_ONCE(1, "cm: %s: no low-level function defined\n",
  58                          __func__);
  59                return -EINVAL;
  60        }
  61
  62        ret = cm_ll_data->split_idlest_reg(idlest_reg, prcm_inst,
  63                                           idlest_reg_id);
  64        *prcm_inst -= cm_base.offset;
  65        return ret;
  66}
  67
  68/**
  69 * omap_cm_wait_module_ready - wait for a module to leave idle or standby
  70 * @part: PRCM partition
  71 * @prcm_mod: PRCM module offset
  72 * @idlest_reg: CM_IDLESTx register
  73 * @idlest_shift: shift of the bit in the CM_IDLEST* register to check
  74 *
  75 * Wait for the PRCM to indicate that the module identified by
  76 * (@prcm_mod, @idlest_id, @idlest_shift) is clocked.  Return 0 upon
  77 * success, -EBUSY if the module doesn't enable in time, or -EINVAL if
  78 * no per-SoC wait_module_ready() function pointer has been registered
  79 * or if the idlest register is unknown on the SoC.
  80 */
  81int omap_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_reg,
  82                              u8 idlest_shift)
  83{
  84        if (!cm_ll_data->wait_module_ready) {
  85                WARN_ONCE(1, "cm: %s: no low-level function defined\n",
  86                          __func__);
  87                return -EINVAL;
  88        }
  89
  90        return cm_ll_data->wait_module_ready(part, prcm_mod, idlest_reg,
  91                                             idlest_shift);
  92}
  93
  94/**
  95 * omap_cm_wait_module_idle - wait for a module to enter idle or standby
  96 * @part: PRCM partition
  97 * @prcm_mod: PRCM module offset
  98 * @idlest_reg: CM_IDLESTx register
  99 * @idlest_shift: shift of the bit in the CM_IDLEST* register to check
 100 *
 101 * Wait for the PRCM to indicate that the module identified by
 102 * (@prcm_mod, @idlest_id, @idlest_shift) is no longer clocked.  Return
 103 * 0 upon success, -EBUSY if the module doesn't enable in time, or
 104 * -EINVAL if no per-SoC wait_module_idle() function pointer has been
 105 * registered or if the idlest register is unknown on the SoC.
 106 */
 107int omap_cm_wait_module_idle(u8 part, s16 prcm_mod, u16 idlest_reg,
 108                             u8 idlest_shift)
 109{
 110        if (!cm_ll_data->wait_module_idle) {
 111                WARN_ONCE(1, "cm: %s: no low-level function defined\n",
 112                          __func__);
 113                return -EINVAL;
 114        }
 115
 116        return cm_ll_data->wait_module_idle(part, prcm_mod, idlest_reg,
 117                                            idlest_shift);
 118}
 119
 120/**
 121 * omap_cm_module_enable - enable a module
 122 * @mode: target mode for the module
 123 * @part: PRCM partition
 124 * @inst: PRCM instance
 125 * @clkctrl_offs: CM_CLKCTRL register offset for the module
 126 *
 127 * Enables clocks for a module identified by (@part, @inst, @clkctrl_offs)
 128 * making its IO space accessible. Return 0 upon success, -EINVAL if no
 129 * per-SoC module_enable() function pointer has been registered.
 130 */
 131int omap_cm_module_enable(u8 mode, u8 part, u16 inst, u16 clkctrl_offs)
 132{
 133        if (!cm_ll_data->module_enable) {
 134                WARN_ONCE(1, "cm: %s: no low-level function defined\n",
 135                          __func__);
 136                return -EINVAL;
 137        }
 138
 139        cm_ll_data->module_enable(mode, part, inst, clkctrl_offs);
 140        return 0;
 141}
 142
 143/**
 144 * omap_cm_module_disable - disable a module
 145 * @part: PRCM partition
 146 * @inst: PRCM instance
 147 * @clkctrl_offs: CM_CLKCTRL register offset for the module
 148 *
 149 * Disables clocks for a module identified by (@part, @inst, @clkctrl_offs)
 150 * makings its IO space inaccessible. Return 0 upon success, -EINVAL if
 151 * no per-SoC module_disable() function pointer has been registered.
 152 */
 153int omap_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs)
 154{
 155        if (!cm_ll_data->module_disable) {
 156                WARN_ONCE(1, "cm: %s: no low-level function defined\n",
 157                          __func__);
 158                return -EINVAL;
 159        }
 160
 161        cm_ll_data->module_disable(part, inst, clkctrl_offs);
 162        return 0;
 163}
 164
 165u32 omap_cm_xlate_clkctrl(u8 part, u16 inst, u16 clkctrl_offs)
 166{
 167        if (!cm_ll_data->xlate_clkctrl) {
 168                WARN_ONCE(1, "cm: %s: no low-level function defined\n",
 169                          __func__);
 170                return 0;
 171        }
 172        return cm_ll_data->xlate_clkctrl(part, inst, clkctrl_offs);
 173}
 174
 175/**
 176 * cm_register - register per-SoC low-level data with the CM
 177 * @cld: low-level per-SoC OMAP CM data & function pointers to register
 178 *
 179 * Register per-SoC low-level OMAP CM data and function pointers with
 180 * the OMAP CM common interface.  The caller must keep the data
 181 * pointed to by @cld valid until it calls cm_unregister() and
 182 * it returns successfully.  Returns 0 upon success, -EINVAL if @cld
 183 * is NULL, or -EEXIST if cm_register() has already been called
 184 * without an intervening cm_unregister().
 185 */
 186int cm_register(const struct cm_ll_data *cld)
 187{
 188        if (!cld)
 189                return -EINVAL;
 190
 191        if (cm_ll_data != &null_cm_ll_data)
 192                return -EEXIST;
 193
 194        cm_ll_data = cld;
 195
 196        return 0;
 197}
 198
 199/**
 200 * cm_unregister - unregister per-SoC low-level data & function pointers
 201 * @cld: low-level per-SoC OMAP CM data & function pointers to unregister
 202 *
 203 * Unregister per-SoC low-level OMAP CM data and function pointers
 204 * that were previously registered with cm_register().  The
 205 * caller may not destroy any of the data pointed to by @cld until
 206 * this function returns successfully.  Returns 0 upon success, or
 207 * -EINVAL if @cld is NULL or if @cld does not match the struct
 208 * cm_ll_data * previously registered by cm_register().
 209 */
 210int cm_unregister(const struct cm_ll_data *cld)
 211{
 212        if (!cld || cm_ll_data != cld)
 213                return -EINVAL;
 214
 215        cm_ll_data = &null_cm_ll_data;
 216
 217        return 0;
 218}
 219
 220#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \
 221        defined(CONFIG_SOC_DRA7XX)
 222static struct omap_prcm_init_data cm_data __initdata = {
 223        .index = TI_CLKM_CM,
 224        .init = omap4_cm_init,
 225};
 226
 227static struct omap_prcm_init_data cm2_data __initdata = {
 228        .index = TI_CLKM_CM2,
 229        .init = omap4_cm_init,
 230};
 231#endif
 232
 233#ifdef CONFIG_ARCH_OMAP2
 234static struct omap_prcm_init_data omap2_prcm_data __initdata = {
 235        .index = TI_CLKM_CM,
 236        .init = omap2xxx_cm_init,
 237        .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE,
 238};
 239#endif
 240
 241#ifdef CONFIG_ARCH_OMAP3
 242static struct omap_prcm_init_data omap3_cm_data __initdata = {
 243        .index = TI_CLKM_CM,
 244        .init = omap3xxx_cm_init,
 245        .flags = CM_SINGLE_INSTANCE,
 246
 247        /*
 248         * IVA2 offset is a negative value, must offset the cm_base address
 249         * by this to get it to positive side on the iomap
 250         */
 251        .offset = -OMAP3430_IVA2_MOD,
 252};
 253#endif
 254
 255#if defined(CONFIG_SOC_AM33XX) || defined(CONFIG_SOC_TI81XX)
 256static struct omap_prcm_init_data am3_prcm_data __initdata = {
 257        .index = TI_CLKM_CM,
 258        .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE,
 259        .init = am33xx_cm_init,
 260};
 261#endif
 262
 263#ifdef CONFIG_SOC_AM43XX
 264static struct omap_prcm_init_data am4_prcm_data __initdata = {
 265        .index = TI_CLKM_CM,
 266        .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE,
 267        .init = omap4_cm_init,
 268};
 269#endif
 270
 271static const struct of_device_id omap_cm_dt_match_table[] __initconst = {
 272#ifdef CONFIG_ARCH_OMAP2
 273        { .compatible = "ti,omap2-prcm", .data = &omap2_prcm_data },
 274#endif
 275#ifdef CONFIG_ARCH_OMAP3
 276        { .compatible = "ti,omap3-cm", .data = &omap3_cm_data },
 277#endif
 278#ifdef CONFIG_ARCH_OMAP4
 279        { .compatible = "ti,omap4-cm1", .data = &cm_data },
 280        { .compatible = "ti,omap4-cm2", .data = &cm2_data },
 281#endif
 282#ifdef CONFIG_SOC_OMAP5
 283        { .compatible = "ti,omap5-cm-core-aon", .data = &cm_data },
 284        { .compatible = "ti,omap5-cm-core", .data = &cm2_data },
 285#endif
 286#ifdef CONFIG_SOC_DRA7XX
 287        { .compatible = "ti,dra7-cm-core-aon", .data = &cm_data },
 288        { .compatible = "ti,dra7-cm-core", .data = &cm2_data },
 289#endif
 290#ifdef CONFIG_SOC_AM33XX
 291        { .compatible = "ti,am3-prcm", .data = &am3_prcm_data },
 292#endif
 293#ifdef CONFIG_SOC_AM43XX
 294        { .compatible = "ti,am4-prcm", .data = &am4_prcm_data },
 295#endif
 296#ifdef CONFIG_SOC_TI81XX
 297        { .compatible = "ti,dm814-prcm", .data = &am3_prcm_data },
 298        { .compatible = "ti,dm816-prcm", .data = &am3_prcm_data },
 299#endif
 300        { }
 301};
 302
 303/**
 304 * omap2_cm_base_init - initialize iomappings for the CM drivers
 305 *
 306 * Detects and initializes the iomappings for the CM driver, based
 307 * on the DT data. Returns 0 in success, negative error value
 308 * otherwise.
 309 */
 310int __init omap2_cm_base_init(void)
 311{
 312        struct device_node *np;
 313        const struct of_device_id *match;
 314        struct omap_prcm_init_data *data;
 315        struct resource res;
 316        int ret;
 317        struct omap_domain_base *mem = NULL;
 318
 319        for_each_matching_node_and_match(np, omap_cm_dt_match_table, &match) {
 320                data = (struct omap_prcm_init_data *)match->data;
 321
 322                ret = of_address_to_resource(np, 0, &res);
 323                if (ret)
 324                        return ret;
 325
 326                if (data->index == TI_CLKM_CM)
 327                        mem = &cm_base;
 328
 329                if (data->index == TI_CLKM_CM2)
 330                        mem = &cm2_base;
 331
 332                data->mem = ioremap(res.start, resource_size(&res));
 333
 334                if (mem) {
 335                        mem->pa = res.start + data->offset;
 336                        mem->va = data->mem + data->offset;
 337                        mem->offset = data->offset;
 338                }
 339
 340                data->np = np;
 341
 342                if (data->init && (data->flags & CM_SINGLE_INSTANCE ||
 343                                   (cm_base.va && cm2_base.va)))
 344                        data->init(data);
 345        }
 346
 347        return 0;
 348}
 349
 350/**
 351 * omap_cm_init - low level init for the CM drivers
 352 *
 353 * Initializes the low level clock infrastructure for CM drivers.
 354 * Returns 0 in success, negative error value in failure.
 355 */
 356int __init omap_cm_init(void)
 357{
 358        struct device_node *np;
 359        const struct of_device_id *match;
 360        const struct omap_prcm_init_data *data;
 361        int ret;
 362
 363        for_each_matching_node_and_match(np, omap_cm_dt_match_table, &match) {
 364                data = match->data;
 365
 366                if (data->flags & CM_NO_CLOCKS)
 367                        continue;
 368
 369                ret = omap2_clk_provider_init(np, data->index, NULL, data->mem);
 370                if (ret)
 371                        return ret;
 372        }
 373
 374        return 0;
 375}
 376