162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Clock driver for Keystone 2 based devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Texas Instruments. 662306a36Sopenharmony_ci * Murali Karicheri <m-karicheri2@ti.com> 762306a36Sopenharmony_ci * Santosh Shilimkar <santosh.shilimkar@ti.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* PSC register offsets */ 1862306a36Sopenharmony_ci#define PTCMD 0x120 1962306a36Sopenharmony_ci#define PTSTAT 0x128 2062306a36Sopenharmony_ci#define PDSTAT 0x200 2162306a36Sopenharmony_ci#define PDCTL 0x300 2262306a36Sopenharmony_ci#define MDSTAT 0x800 2362306a36Sopenharmony_ci#define MDCTL 0xa00 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* PSC module states */ 2662306a36Sopenharmony_ci#define PSC_STATE_SWRSTDISABLE 0 2762306a36Sopenharmony_ci#define PSC_STATE_SYNCRST 1 2862306a36Sopenharmony_ci#define PSC_STATE_DISABLE 2 2962306a36Sopenharmony_ci#define PSC_STATE_ENABLE 3 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MDSTAT_STATE_MASK 0x3f 3262306a36Sopenharmony_ci#define MDSTAT_MCKOUT BIT(12) 3362306a36Sopenharmony_ci#define PDSTAT_STATE_MASK 0x1f 3462306a36Sopenharmony_ci#define MDCTL_FORCE BIT(31) 3562306a36Sopenharmony_ci#define MDCTL_LRESET BIT(8) 3662306a36Sopenharmony_ci#define PDCTL_NEXT BIT(0) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Maximum timeout to bail out state transition for module */ 3962306a36Sopenharmony_ci#define STATE_TRANS_MAX_COUNT 0xffff 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void __iomem *domain_transition_base; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/** 4462306a36Sopenharmony_ci * struct clk_psc_data - PSC data 4562306a36Sopenharmony_ci * @control_base: Base address for a PSC control 4662306a36Sopenharmony_ci * @domain_base: Base address for a PSC domain 4762306a36Sopenharmony_ci * @domain_id: PSC domain id number 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistruct clk_psc_data { 5062306a36Sopenharmony_ci void __iomem *control_base; 5162306a36Sopenharmony_ci void __iomem *domain_base; 5262306a36Sopenharmony_ci u32 domain_id; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * struct clk_psc - PSC clock structure 5762306a36Sopenharmony_ci * @hw: clk_hw for the psc 5862306a36Sopenharmony_ci * @psc_data: PSC driver specific data 5962306a36Sopenharmony_ci * @lock: Spinlock used by the driver 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistruct clk_psc { 6262306a36Sopenharmony_ci struct clk_hw hw; 6362306a36Sopenharmony_ci struct clk_psc_data *psc_data; 6462306a36Sopenharmony_ci spinlock_t *lock; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(psc_lock); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define to_clk_psc(_hw) container_of(_hw, struct clk_psc, hw) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void psc_config(void __iomem *control_base, void __iomem *domain_base, 7262306a36Sopenharmony_ci u32 next_state, u32 domain_id) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci u32 ptcmd, pdstat, pdctl, mdstat, mdctl, ptstat; 7562306a36Sopenharmony_ci u32 count = STATE_TRANS_MAX_COUNT; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci mdctl = readl(control_base + MDCTL); 7862306a36Sopenharmony_ci mdctl &= ~MDSTAT_STATE_MASK; 7962306a36Sopenharmony_ci mdctl |= next_state; 8062306a36Sopenharmony_ci /* For disable, we always put the module in local reset */ 8162306a36Sopenharmony_ci if (next_state == PSC_STATE_DISABLE) 8262306a36Sopenharmony_ci mdctl &= ~MDCTL_LRESET; 8362306a36Sopenharmony_ci writel(mdctl, control_base + MDCTL); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci pdstat = readl(domain_base + PDSTAT); 8662306a36Sopenharmony_ci if (!(pdstat & PDSTAT_STATE_MASK)) { 8762306a36Sopenharmony_ci pdctl = readl(domain_base + PDCTL); 8862306a36Sopenharmony_ci pdctl |= PDCTL_NEXT; 8962306a36Sopenharmony_ci writel(pdctl, domain_base + PDCTL); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ptcmd = 1 << domain_id; 9362306a36Sopenharmony_ci writel(ptcmd, domain_transition_base + PTCMD); 9462306a36Sopenharmony_ci do { 9562306a36Sopenharmony_ci ptstat = readl(domain_transition_base + PTSTAT); 9662306a36Sopenharmony_ci } while (((ptstat >> domain_id) & 1) && count--); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci count = STATE_TRANS_MAX_COUNT; 9962306a36Sopenharmony_ci do { 10062306a36Sopenharmony_ci mdstat = readl(control_base + MDSTAT); 10162306a36Sopenharmony_ci } while (!((mdstat & MDSTAT_STATE_MASK) == next_state) && count--); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int keystone_clk_is_enabled(struct clk_hw *hw) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct clk_psc *psc = to_clk_psc(hw); 10762306a36Sopenharmony_ci struct clk_psc_data *data = psc->psc_data; 10862306a36Sopenharmony_ci u32 mdstat = readl(data->control_base + MDSTAT); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return (mdstat & MDSTAT_MCKOUT) ? 1 : 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int keystone_clk_enable(struct clk_hw *hw) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct clk_psc *psc = to_clk_psc(hw); 11662306a36Sopenharmony_ci struct clk_psc_data *data = psc->psc_data; 11762306a36Sopenharmony_ci unsigned long flags = 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (psc->lock) 12062306a36Sopenharmony_ci spin_lock_irqsave(psc->lock, flags); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci psc_config(data->control_base, data->domain_base, 12362306a36Sopenharmony_ci PSC_STATE_ENABLE, data->domain_id); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (psc->lock) 12662306a36Sopenharmony_ci spin_unlock_irqrestore(psc->lock, flags); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void keystone_clk_disable(struct clk_hw *hw) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct clk_psc *psc = to_clk_psc(hw); 13462306a36Sopenharmony_ci struct clk_psc_data *data = psc->psc_data; 13562306a36Sopenharmony_ci unsigned long flags = 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (psc->lock) 13862306a36Sopenharmony_ci spin_lock_irqsave(psc->lock, flags); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci psc_config(data->control_base, data->domain_base, 14162306a36Sopenharmony_ci PSC_STATE_DISABLE, data->domain_id); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (psc->lock) 14462306a36Sopenharmony_ci spin_unlock_irqrestore(psc->lock, flags); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic const struct clk_ops clk_psc_ops = { 14862306a36Sopenharmony_ci .enable = keystone_clk_enable, 14962306a36Sopenharmony_ci .disable = keystone_clk_disable, 15062306a36Sopenharmony_ci .is_enabled = keystone_clk_is_enabled, 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/** 15462306a36Sopenharmony_ci * clk_register_psc - register psc clock 15562306a36Sopenharmony_ci * @dev: device that is registering this clock 15662306a36Sopenharmony_ci * @name: name of this clock 15762306a36Sopenharmony_ci * @parent_name: name of clock's parent 15862306a36Sopenharmony_ci * @psc_data: platform data to configure this clock 15962306a36Sopenharmony_ci * @lock: spinlock used by this clock 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_cistatic struct clk *clk_register_psc(struct device *dev, 16262306a36Sopenharmony_ci const char *name, 16362306a36Sopenharmony_ci const char *parent_name, 16462306a36Sopenharmony_ci struct clk_psc_data *psc_data, 16562306a36Sopenharmony_ci spinlock_t *lock) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct clk_init_data init; 16862306a36Sopenharmony_ci struct clk_psc *psc; 16962306a36Sopenharmony_ci struct clk *clk; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci psc = kzalloc(sizeof(*psc), GFP_KERNEL); 17262306a36Sopenharmony_ci if (!psc) 17362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci init.name = name; 17662306a36Sopenharmony_ci init.ops = &clk_psc_ops; 17762306a36Sopenharmony_ci init.flags = 0; 17862306a36Sopenharmony_ci init.parent_names = (parent_name ? &parent_name : NULL); 17962306a36Sopenharmony_ci init.num_parents = (parent_name ? 1 : 0); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci psc->psc_data = psc_data; 18262306a36Sopenharmony_ci psc->lock = lock; 18362306a36Sopenharmony_ci psc->hw.init = &init; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci clk = clk_register(NULL, &psc->hw); 18662306a36Sopenharmony_ci if (IS_ERR(clk)) 18762306a36Sopenharmony_ci kfree(psc); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return clk; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/** 19362306a36Sopenharmony_ci * of_psc_clk_init - initialize psc clock through DT 19462306a36Sopenharmony_ci * @node: device tree node for this clock 19562306a36Sopenharmony_ci * @lock: spinlock used by this clock 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic void __init of_psc_clk_init(struct device_node *node, spinlock_t *lock) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci const char *clk_name = node->name; 20062306a36Sopenharmony_ci const char *parent_name; 20162306a36Sopenharmony_ci struct clk_psc_data *data; 20262306a36Sopenharmony_ci struct clk *clk; 20362306a36Sopenharmony_ci int i; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 20662306a36Sopenharmony_ci if (!data) { 20762306a36Sopenharmony_ci pr_err("%s: Out of memory\n", __func__); 20862306a36Sopenharmony_ci return; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci i = of_property_match_string(node, "reg-names", "control"); 21262306a36Sopenharmony_ci data->control_base = of_iomap(node, i); 21362306a36Sopenharmony_ci if (!data->control_base) { 21462306a36Sopenharmony_ci pr_err("%s: control ioremap failed\n", __func__); 21562306a36Sopenharmony_ci goto out; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci i = of_property_match_string(node, "reg-names", "domain"); 21962306a36Sopenharmony_ci data->domain_base = of_iomap(node, i); 22062306a36Sopenharmony_ci if (!data->domain_base) { 22162306a36Sopenharmony_ci pr_err("%s: domain ioremap failed\n", __func__); 22262306a36Sopenharmony_ci goto unmap_ctrl; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci of_property_read_u32(node, "domain-id", &data->domain_id); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Domain transition registers at fixed address space of domain_id 0 */ 22862306a36Sopenharmony_ci if (!domain_transition_base && !data->domain_id) 22962306a36Sopenharmony_ci domain_transition_base = data->domain_base; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci of_property_read_string(node, "clock-output-names", &clk_name); 23262306a36Sopenharmony_ci parent_name = of_clk_get_parent_name(node, 0); 23362306a36Sopenharmony_ci if (!parent_name) { 23462306a36Sopenharmony_ci pr_err("%s: Parent clock not found\n", __func__); 23562306a36Sopenharmony_ci goto unmap_domain; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci clk = clk_register_psc(NULL, clk_name, parent_name, data, lock); 23962306a36Sopenharmony_ci if (!IS_ERR(clk)) { 24062306a36Sopenharmony_ci of_clk_add_provider(node, of_clk_src_simple_get, clk); 24162306a36Sopenharmony_ci return; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci pr_err("%s: error registering clk %pOFn\n", __func__, node); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciunmap_domain: 24762306a36Sopenharmony_ci iounmap(data->domain_base); 24862306a36Sopenharmony_ciunmap_ctrl: 24962306a36Sopenharmony_ci iounmap(data->control_base); 25062306a36Sopenharmony_ciout: 25162306a36Sopenharmony_ci kfree(data); 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/** 25662306a36Sopenharmony_ci * of_keystone_psc_clk_init - initialize psc clock through DT 25762306a36Sopenharmony_ci * @node: device tree node for this clock 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_cistatic void __init of_keystone_psc_clk_init(struct device_node *node) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci of_psc_clk_init(node, &psc_lock); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ciCLK_OF_DECLARE(keystone_gate_clk, "ti,keystone,psc-clock", 26462306a36Sopenharmony_ci of_keystone_psc_clk_init); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 26762306a36Sopenharmony_ciMODULE_DESCRIPTION("Clock driver for Keystone 2 based devices"); 26862306a36Sopenharmony_ciMODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>"); 26962306a36Sopenharmony_ciMODULE_AUTHOR("Santosh Shilimkar <santosh.shilimkar@ti.com>"); 270