/* * Copyright (C) 2016 Freescale Semiconductor, Inc. * Copyright 2017-2018 NXP * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include "clk-imx8.h" /* * DOC: basic gatable clock which can gate and ungate it's ouput * * Traits of this clock: * prepare - clk_(un)prepare only ensures parent is (un)prepared * enable - clk_enable and clk_disable are functional & control gating * rate - inherits rate from parent. No clk_set_rate support * parent - fixed parent. No clk_set_parent support */ #define CLK_GATE_SCU_HW_SW_EN (BIT(0) | BIT(1)) #define CLK_GATE_SCU_SW_EN BIT(1) struct clk_gate_scu { struct clk_hw hw; void __iomem *reg; u8 bit_idx; bool hw_gate; u8 flags; spinlock_t *lock; sc_rsrc_t rsrc_id; sc_pm_clk_t clk_type; }; struct clk_gate2_scu { struct clk_hw hw; void __iomem *reg; u8 bit_idx; u8 flags; spinlock_t *lock; char *pd_name; struct generic_pm_domain *pd; }; struct clk_gate3_scu { struct clk_hw hw; spinlock_t *lock; sc_rsrc_t rsrc_id; sc_ctrl_t gpr_id; bool invert; }; #define to_clk_gate_scu(_hw) container_of(_hw, struct clk_gate_scu, hw) #define to_clk_gate2_scu(_hw) container_of(_hw, struct clk_gate2_scu, hw) #define to_clk_gate3_scu(_hw) container_of(_hw, struct clk_gate3_scu, hw) /* Write to the LPCG bits. */ static int clk_gate_scu_enable(struct clk_hw *hw) { struct clk_gate_scu *gate = to_clk_gate_scu(hw); u32 reg; if (!ccm_ipc_handle) return -1; if (gate->reg) { reg = readl(gate->reg); if (gate->hw_gate) reg |= (CLK_GATE_SCU_HW_SW_EN << gate->bit_idx); else reg |= (CLK_GATE_SCU_SW_EN << gate->bit_idx); writel(reg, gate->reg); } return 0; } /* Write to the LPCG bits. */ static void clk_gate_scu_disable(struct clk_hw *hw) { struct clk_gate_scu *gate = to_clk_gate_scu(hw); u32 reg; if (!ccm_ipc_handle) return; if (gate->reg) { reg = readl(gate->reg); if (gate->hw_gate) reg &= ~(CLK_GATE_SCU_HW_SW_EN << gate->bit_idx); else reg &= ~(CLK_GATE_SCU_SW_EN << gate->bit_idx); writel(reg, gate->reg); } } static int clk_gate_scu_prepare(struct clk_hw *hw) { struct clk_gate_scu *gate = to_clk_gate_scu(hw); sc_err_t sci_err = SC_ERR_NONE; if (!ccm_ipc_handle) return -1; /* Enable the clock at the DSC slice level */ sci_err = sc_pm_clock_enable(ccm_ipc_handle, gate->rsrc_id, gate->clk_type, true, gate->hw_gate); if (sci_err != SC_ERR_NONE) return -EINVAL; return 0; } static void clk_gate_scu_unprepare(struct clk_hw *hw) { struct clk_gate_scu *gate = to_clk_gate_scu(hw); sc_err_t sci_err; if (!ccm_ipc_handle) return; sci_err = sc_pm_clock_enable(ccm_ipc_handle, gate->rsrc_id, gate->clk_type, false, false); if (sci_err) pr_err("clk gate scu unprepare clk fail!\n"); } static unsigned long clk_gate_scu_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_gate_scu *clk = to_clk_gate_scu(hw); sc_err_t sci_err; sc_pm_clock_rate_t rate = 0; if (!ccm_ipc_handle) return 0; sci_err = sc_pm_get_clock_rate(ccm_ipc_handle, clk->rsrc_id, clk->clk_type, &rate); return sci_err ? 0 : rate; } static struct clk_ops clk_gate_scu_ops = { .prepare = clk_gate_scu_prepare, .unprepare = clk_gate_scu_unprepare, .enable = clk_gate_scu_enable, .disable = clk_gate_scu_disable, .recalc_rate = clk_gate_scu_recalc_rate, }; struct clk *clk_register_gate_scu(struct device *dev, const char *name, const char *parent_name, unsigned long flags, u8 clk_gate_scu_flags, spinlock_t *lock, sc_rsrc_t rsrc_id, sc_pm_clk_t clk_type, void __iomem *reg, u8 bit_idx, bool hw_gate) { struct clk_gate_scu *gate; struct clk *clk; struct clk_init_data init; if (!imx8_clk_is_resource_owned(rsrc_id)) { pr_debug("skip clk %s rsrc %d not owned\n", name, rsrc_id); return ERR_PTR(-ENODEV); } gate = kzalloc(sizeof(struct clk_gate_scu), GFP_KERNEL); if (!gate) return ERR_PTR(-ENOMEM); /* struct clk_gate_scu assignments */ gate->flags = clk_gate_scu_flags; gate->lock = lock; gate->rsrc_id = rsrc_id; gate->clk_type = clk_type; if (reg != NULL) gate->reg = ioremap((phys_addr_t)reg, SZ_64K); else gate->reg = NULL; gate->bit_idx = bit_idx; gate->hw_gate = hw_gate; init.name = name; init.ops = &clk_gate_scu_ops; init.flags = flags; init.parent_names = parent_name ? &parent_name : NULL; init.num_parents = parent_name ? 1 : 0; gate->hw.init = &init; clk = clk_register(dev, &gate->hw); if (IS_ERR(clk)) { iounmap(gate->reg); kfree(gate); } return clk; } /* Get the power domain associated with the clock from the device tree. */ static void populate_gate_pd(struct clk_gate2_scu *clk) { struct device_node *np; struct of_phandle_args pd_args; np = of_find_node_by_name(NULL, clk->pd_name); if (np) { pd_args.np = np; pd_args.args_count = 0; clk->pd = genpd_get_from_provider(&pd_args); if (IS_ERR(clk->pd)) pr_warn("%s: failed to get pd\n", __func__); } } /* Write to the LPCG bits. */ static int clk_gate2_scu_enable(struct clk_hw *hw) { struct clk_gate2_scu *gate = to_clk_gate2_scu(hw); u32 reg; if (!ccm_ipc_handle) return -1; if (gate->pd == NULL && gate->pd_name) populate_gate_pd(gate); if (IS_ERR_OR_NULL(gate->pd)) return -1; if (gate->pd->status != GPD_STATE_ACTIVE) return -1; if (gate->reg) { reg = readl(gate->reg); reg |= (0x2 << gate->bit_idx); writel(reg, gate->reg); } return 0; } /* Write to the LPCG bits. */ static void clk_gate2_scu_disable(struct clk_hw *hw) { struct clk_gate2_scu *gate = to_clk_gate2_scu(hw); u32 reg; if (!ccm_ipc_handle) return; if (gate->pd == NULL && gate->pd_name) populate_gate_pd(gate); if (IS_ERR_OR_NULL(gate->pd)) return; if (gate->pd->status != GPD_STATE_ACTIVE) return; if (gate->reg) { reg = readl(gate->reg); reg &= ~(0x2 << gate->bit_idx); writel(reg, gate->reg); } } static int clk_gate2_scu_is_enabled(struct clk_hw *hw) { struct clk_gate2_scu *gate = to_clk_gate2_scu(hw); u32 val; if (gate->pd == NULL && gate->pd_name) populate_gate_pd(gate); if (IS_ERR_OR_NULL(gate->pd)) return 0; if (gate->pd->status != GPD_STATE_ACTIVE) return 0; if (gate->reg) { val = readl(gate->reg); if (((val >> gate->bit_idx) & 2) == 2) return 1; } return 0; } static struct clk_ops clk_gate2_scu_ops = { .enable = clk_gate2_scu_enable, .disable = clk_gate2_scu_disable, .is_enabled = clk_gate2_scu_is_enabled, }; struct clk *clk_register_gate2_scu(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock, const char *pd_name) { struct clk_gate2_scu *gate; struct clk *clk; struct clk_init_data init; gate = kzalloc(sizeof(struct clk_gate2_scu), GFP_KERNEL); if (!gate) return ERR_PTR(-ENOMEM); /* struct clk_gate_scu assignments */ gate->flags = clk_gate_flags; gate->lock = lock; if (reg != NULL) gate->reg = ioremap((phys_addr_t)reg, SZ_64K); else gate->reg = NULL; gate->bit_idx = bit_idx; gate->pd = NULL; gate->pd_name = NULL; if (pd_name) { gate->pd_name = kzalloc(strlen(pd_name) + 1, GFP_KERNEL); strcpy(gate->pd_name, pd_name); } init.name = name; init.ops = &clk_gate2_scu_ops; init.flags = flags; init.parent_names = parent_name ? &parent_name : NULL; init.num_parents = parent_name ? 1 : 0; gate->hw.init = &init; clk = clk_register(dev, &gate->hw); if (IS_ERR(clk)) { iounmap(gate->reg); kfree(gate->pd_name); kfree(gate); } return clk; } static int clk_gate3_scu_prepare(struct clk_hw *hw) { struct clk_gate3_scu *gate = to_clk_gate3_scu(hw); uint32_t val; if (!ccm_ipc_handle) return -1; val = (gate->invert) ? 0 : 1; return sc_misc_set_control(ccm_ipc_handle, gate->rsrc_id, gate->gpr_id, val); } /* Write to the LPCG bits. */ static void clk_gate3_scu_unprepare(struct clk_hw *hw) { struct clk_gate3_scu *gate = to_clk_gate3_scu(hw); uint32_t val; if (!ccm_ipc_handle) return; val = (gate->invert) ? 1 : 0; sc_misc_set_control(ccm_ipc_handle, gate->rsrc_id, gate->gpr_id, val); } static int clk_gate3_scu_is_prepared(struct clk_hw *hw) { struct clk_gate3_scu *gate = to_clk_gate3_scu(hw); uint32_t val; if (!ccm_ipc_handle) return -1; sc_misc_get_control(ccm_ipc_handle, gate->rsrc_id, gate->gpr_id, &val); val &= 1; if (gate->invert) return 1 - val; return val; } static struct clk_ops clk_gate3_scu_ops = { .prepare = clk_gate3_scu_prepare, .unprepare = clk_gate3_scu_unprepare, .is_prepared = clk_gate3_scu_is_prepared, }; struct clk *clk_register_gate3_scu(struct device *dev, const char *name, const char *parent_name, spinlock_t *lock, sc_rsrc_t rsrc_id, sc_ctrl_t gpr_id, bool invert_flag) { struct clk_gate3_scu *gate; struct clk *clk; struct clk_init_data init; if (!imx8_clk_is_resource_owned(rsrc_id)) { pr_debug("skip clk %s rsrc %d not owned\n", name, rsrc_id); return ERR_PTR(-ENODEV); } gate = kzalloc(sizeof(struct clk_gate_scu), GFP_KERNEL); if (!gate) return ERR_PTR(-ENOMEM); /* struct clk_gate_scu assignments */ gate->lock = lock; gate->rsrc_id = rsrc_id; gate->gpr_id = gpr_id; gate->invert = invert_flag; init.name = name; init.ops = &clk_gate3_scu_ops; init.flags = 0; init.parent_names = parent_name ? &parent_name : NULL; init.num_parents = parent_name ? 1 : 0; gate->hw.init = &init; clk = clk_register(dev, &gate->hw); if (IS_ERR(clk)) kfree(gate); return clk; }