From patchwork Thu Mar 21 17:35:40 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tero Kristo X-Patchwork-Id: 2315991 Return-Path: X-Original-To: patchwork-linux-omap@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork2.kernel.org (Postfix) with ESMTP id 3FEB4DF264 for ; Thu, 21 Mar 2013 17:36:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751809Ab3CURgg (ORCPT ); Thu, 21 Mar 2013 13:36:36 -0400 Received: from bear.ext.ti.com ([192.94.94.41]:44954 "EHLO bear.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751558Ab3CURgf (ORCPT ); Thu, 21 Mar 2013 13:36:35 -0400 Received: from dlelxv30.itg.ti.com ([172.17.2.17]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id r2LHaKEK023824; Thu, 21 Mar 2013 12:36:20 -0500 Received: from DLEE70.ent.ti.com (dlee70.ent.ti.com [157.170.170.113]) by dlelxv30.itg.ti.com (8.13.8/8.13.8) with ESMTP id r2LHaKGi025808; Thu, 21 Mar 2013 12:36:20 -0500 Received: from dlelxv22.itg.ti.com (172.17.1.197) by DLEE70.ent.ti.com (157.170.170.113) with Microsoft SMTP Server id 14.2.342.3; Thu, 21 Mar 2013 12:36:20 -0500 Received: from localhost.localdomain (h79-1.vpn.ti.com [172.24.79.1]) by dlelxv22.itg.ti.com (8.13.8/8.13.8) with ESMTP id r2LHaGee025896; Thu, 21 Mar 2013 12:36:18 -0500 From: Tero Kristo To: , , , CC: , Mike Turquette Subject: [RFC 1/8] RFC: CLK: OMAP: Add basic infrastructure for OMAP clocks Date: Thu, 21 Mar 2013 19:35:40 +0200 Message-ID: <1363887347-4686-2-git-send-email-t-kristo@ti.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1363887347-4686-1-git-send-email-t-kristo@ti.com> References: <1363887347-4686-1-git-send-email-t-kristo@ti.com> MIME-Version: 1.0 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org This patch adds basic infrastructure support for registering clocks under common clock framework. This patch is done in preparation for moving clock data from arch/arm/mach-omap2/ folder under /drivers/clk/omap. Signed-off-by: Tero Kristo Cc: Mike Turquette --- drivers/clk/omap/clk.c | 220 ++++++++++++++++ drivers/clk/omap/clock.h | 645 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 865 insertions(+), 0 deletions(-) create mode 100644 drivers/clk/omap/clk.c create mode 100644 drivers/clk/omap/clock.h diff --git a/drivers/clk/omap/clk.c b/drivers/clk/omap/clk.c new file mode 100644 index 0000000..246f70d --- /dev/null +++ b/drivers/clk/omap/clk.c @@ -0,0 +1,209 @@ +/* + * OMAP clock support + * + * Copyright (c) 2013, Texas Instruments. All rights reserved. + * + * Tero Kristo (t-kristo@ti.com) + * + * Highly based on drivers/clk/tegra/clk.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "clock.h" + +static void __iomem **clk_base; + +static struct clk_ops dummy_ck_ops __initdata = {}; + +static struct clk_hw_omap dummy_ck_hw __initdata = { +}; + +struct omap_init_clk omap_dummy_ck __initdata = { + .name = "dummy_clk", + .ops = &dummy_ck_ops, + .clk_register = omap_clk_register, + .hw = &dummy_ck_hw, +}; + +static struct clksel *omap_clk_init_clksel(struct omap_init_clk *c) +{ + struct clksel *sel; + struct clksel_init *init; + int sz, i; + + init = c->hw->clksel.init; + + if (!init) + return NULL; + + if (init->clksel) + return init->clksel; + + /* Check size for clksel array */ + sz = 0; + while (init[sz].parent_name) { + sz++; + } + + sel = kzalloc(sizeof(struct clksel) * (sz + 1), GFP_KERNEL); + + for (i = 0; i < sz; i++) { + sel[i].parent = clk_get(NULL, init[i].parent_name); + sel[i].rates = init[i].rates; + } + + init->clksel = sel; + + return sel; +} + +static inline void __iomem *omap_clk_reg_to_ptr(struct clk_reginfo *reg) +{ + void __iomem *base; + + base = clk_base[reg->module]; + return base + reg->offset; +} + +static struct dpll_data *omap_clk_init_dpll_data(struct omap_init_clk *c) +{ + struct dpll_data *init = c->hw->dpll_data; + struct dpll_data *dd; + + if (!init) + return NULL; + + dd = kmalloc(sizeof(struct dpll_data), GFP_KERNEL); + + memcpy(dd, init, sizeof(struct dpll_data)); + + dd->clk_bypass.clk = clk_get(NULL, init->clk_bypass.name); + dd->clk_ref.clk = clk_get(NULL, init->clk_ref.name); + dd->mult_div1_reg.ptr = + omap_clk_reg_to_ptr(&init->mult_div1_reg.reginfo); + dd->control_reg.ptr = + omap_clk_reg_to_ptr(&init->control_reg.reginfo); + dd->autoidle_reg.ptr = + omap_clk_reg_to_ptr(&init->autoidle_reg.reginfo); + dd->idlest_reg.ptr = + omap_clk_reg_to_ptr(&init->idlest_reg.reginfo); + + return dd; +} + +struct clk *omap_clk_register(struct omap_init_clk *c) +{ + struct clk_init_data init; + struct clk_hw_omap *hw; + + init.name = c->name; + init.ops = c->ops; + init.parent_names = c->parent_names; + init.num_parents = c->num_parents; + init.flags = 0; + + hw = kzalloc(sizeof(struct clk_hw_omap), GFP_KERNEL); + + memcpy(hw, c->hw, sizeof(struct clk_hw_omap)); + + hw->hw.init = &init; + + hw->clksel.ptr = omap_clk_init_clksel(c); + + hw->dpll_data = omap_clk_init_dpll_data(c); + + if (c->hw->clksel_reg.reginfo.module) + hw->clksel_reg.ptr = + omap_clk_reg_to_ptr(&c->hw->clksel_reg.reginfo); + + if (c->hw->enable_reg.reginfo.module) + hw->enable_reg.ptr = + omap_clk_reg_to_ptr(&c->hw->enable_reg.reginfo); + + return clk_register(NULL, &hw->hw); +} + +struct clk *omap_clk_register_fixed_rate(struct omap_init_clk *c) +{ + return clk_register_fixed_rate(NULL, c->name, c->parent_name, + c->clk_flags, c->rate); +} + +struct clk *omap_clk_register_mux(struct omap_init_clk *c) +{ + return clk_register_mux(NULL, c->name, c->parent_names, c->num_parents, + c->clk_flags, omap_clk_reg_to_ptr(&c->reginfo), + c->shift, c->width, c->clk_sub_flags, c->lock); +} + +struct clk *omap_clk_register_gate(struct omap_init_clk *c) +{ + return clk_register_gate(NULL, c->name, c->parent_name, c->clk_flags, + omap_clk_reg_to_ptr(&c->reginfo), c->shift, + c->clk_sub_flags, c->lock); +} + +struct clk *omap_clk_register_divider(struct omap_init_clk *c) +{ + return clk_register_divider_table(NULL, c->name, c->parent_name, + c->clk_flags, + omap_clk_reg_to_ptr(&c->reginfo), + c->shift, c->width, + c->clk_sub_flags, c->table, c->lock); +} + +struct clk *omap_clk_register_fixed_factor(struct omap_init_clk *c) +{ + return clk_register_fixed_factor(NULL, c->name, c->parent_name, + c->clk_flags, c->mult, c->div); +} + +int omap_clk_register_clks(struct omap_clk *clks, int size, u32 cpu_clkflg, + void __iomem **base) +{ + struct omap_clk *c; + struct clk *clk; + + clk_base = base; + + for (c = clks; c < clks + size; c++) { + if (!(c->cpu & cpu_clkflg)) + continue; + + clk = NULL; + + if (c->clk->clk) { + clk = c->clk->clk; + clk_register_clkdev(clk, c->con_id, c->dev_id); + continue; + } + + /* Register clk based on type */ + clk = c->clk->clk_register(c->clk); + + c->clk->clk = clk; + + if (IS_ERR_OR_NULL(clk)) + pr_err("%s: failed to register clk %s, type=%d\n", + __func__, c->clk->name, c->clk->type); + else if (!clk_register_clkdev(clk, c->con_id, c->dev_id)) + omap2_init_clk_hw_omap_clocks(clk); + } + + return 0; +} diff --git a/drivers/clk/omap/clock.h b/drivers/clk/omap/clock.h new file mode 100644 index 0000000..80d7369 --- /dev/null +++ b/drivers/clk/omap/clock.h @@ -0,0 +1,645 @@ +/* + * OMAP clock support header + * + * Copyright (C) 2005-2009 Texas Instruments, Inc. + * Copyright (C) 2004-2011 Nokia Corporation + * + * Contacts: + * Richard Woodruff + * Paul Walmsley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H +#define __ARCH_ARM_MACH_OMAP2_CLOCK_H + +#include +#include + +#include +#include + +#include + +struct omap_init_clk; + +struct clk_reginfo { + u16 module; + u16 offset; +}; + +struct omap_init_clk { + struct clk *clk; + //const char *dev_id; + const char *name; + const char **parent_names; + const char *parent_name; + int num_parents; + u32 clk_flags; + u8 clk_sub_flags; + u32 rate; + struct clk_reginfo reginfo; + u8 shift; + u8 width; + u16 mult; + u16 div; + u8 type; + struct clk_hw_omap *hw; + const struct clk_ops *ops; + spinlock_t *lock; + const struct clk_div_table *table; + struct clk* (*clk_register)(struct omap_init_clk*); +}; + +struct omap_clk { + u16 cpu; + const char *dev_id; + const char *con_id; + struct omap_init_clk *clk; +}; + +#define CLK(dev, con, ck, cp) \ + { \ + .cpu = cp, \ + .dev_id = dev, \ + .con_id = con, \ + .clk = ck, \ + } + +/* Platform flags for the clkdev-OMAP integration code */ +#define CK_242X (1 << 0) +#define CK_243X (1 << 1) /* 243x, 253x */ +#define CK_3430ES1 (1 << 2) /* 34xxES1 only */ +#define CK_3430ES2PLUS (1 << 3) /* 34xxES2, ES3, non-Sitara 35xx only */ +#define CK_AM35XX (1 << 4) /* Sitara AM35xx */ +#define CK_36XX (1 << 5) /* 36xx/37xx-specific clocks */ +#define CK_443X (1 << 6) +#define CK_TI816X (1 << 7) +#define CK_446X (1 << 8) +#define CK_AM33XX (1 << 9) /* AM33xx specific clocks */ +#define CK_54XX (1 << 10) /* OMAP54xx specific clocks */ + + +#define CK_34XX (CK_3430ES1 | CK_3430ES2PLUS) +#define CK_3XXX (CK_34XX | CK_AM35XX | CK_36XX) + +struct clockdomain; +#define to_clk_hw_omap(_hw) container_of(_hw, struct clk_hw_omap, hw) + +#define OMAP_CLK_FIXED_RATE(_name, _flags, _rate, _ignore) \ + static struct omap_init_clk _name __initdata = { \ + .name = #_name, \ + .clk_flags = _flags, \ + .rate = _rate, \ + .clk_register = omap_clk_register_fixed_rate, \ + }; + +#define OMAP_CLK_GATE(_name, _parent_name, _ignore, _flags, \ + _reg, _shift, _gate_flags, _lock) \ + static struct omap_init_clk _name __initdata = { \ + .name = #_name, \ + .parent_name = _parent_name, \ + .clk_flags = _flags, \ + .reginfo = { _reg }, \ + .shift = _shift, \ + .clk_sub_flags = _gate_flags, \ + .clk_register = omap_clk_register_gate, \ + .lock = _lock, \ + }; + +#define OMAP_CLK_MUX(_name, _parents, _ignore, _flags, _reg, \ + _shift, _width, _mux_flags, _lock) \ + static struct omap_init_clk _name __initdata = { \ + .name = #_name, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .clk_flags = _flags, \ + .reginfo = { _reg }, \ + .shift = _shift, \ + .width = _width, \ + .clk_sub_flags = _mux_flags, \ + .clk_register = omap_clk_register_mux, \ + .lock = _lock, \ + }; + +#define OMAP_CLK(_name, _parents, _ops) \ + static struct omap_init_clk _name __initdata = { \ + .name = #_name, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .hw = &_name##_hw, \ + .ops = &_ops, \ + .clk_register = omap_clk_register, \ + }; + +#define OMAP_CLK_DIVIDER(_name, _parent_name, _ignore, _flags, \ + _reg, _shift, _width, _div_flags, \ + _lock) \ + static struct omap_init_clk _name __initdata = { \ + .name = #_name, \ + .parent_name = _parent_name, \ + .clk_flags = _flags, \ + .reginfo = { _reg }, \ + .shift = _shift, \ + .width = _width, \ + .clk_sub_flags = _div_flags, \ + .table = NULL, \ + .clk_register = omap_clk_register_divider, \ + .lock = _lock, \ + }; + +#define OMAP_CLK_DIVIDER_TABLE(_name, _parent_name, _ignore, \ + _flags, _reg, _shift, _width, \ + _div_flags, _table, _lock) \ + static struct omap_init_clk _name __initdata = { \ + .name = #_name, \ + .parent_name = _parent_name, \ + .clk_flags = _flags, \ + .reginfo = { _reg }, \ + .shift = _shift, \ + .width = _width, \ + .clk_sub_flags = _div_flags, \ + .table = _table, \ + .clk_register = omap_clk_register_divider, \ + .lock = _lock, \ + }; + +#define OMAP_CLK_FIXED_FACTOR(_name, _parent_name, _ignore, \ + _flags, _mult, _div) \ + static struct omap_init_clk _name __initdata = { \ + .name = #_name, \ + .parent_name = _parent_name, \ + .clk_flags = _flags, \ + .mult = _mult, \ + .div = _div, \ + .clk_register = omap_clk_register_fixed_factor, \ + }; + +#define DEFINE_STRUCT_CLK_HW_OMAP(_name, _clkdm_name) \ + static struct clk_hw_omap _name##_hw __initdata = { \ + .clkdm_name = _clkdm_name, \ + }; + +#define DEFINE_CLK_OMAP_MUX(_name, _clkdm_name, _clksel, \ + _reg, _clksel_mask, _parent_names, \ + _ops) \ + static struct clk_hw_omap _name##_hw __initdata = { \ + .clksel = { .init = _clksel }, \ + .clksel_reg = { .reginfo = { _reg } }, \ + .clksel_mask = _clksel_mask, \ + .clkdm_name = _clkdm_name, \ + }; \ + OMAP_CLK(_name, _parent_names, _ops); + +#define DEFINE_CLK_OMAP_MUX_GATE(_name, _clkdm_name, _clksel, \ + _clksel_reg, _clksel_mask, \ + _enable_reg, _enable_bit, \ + _hwops, _parent_names, _ops) \ + static struct clk_hw_omap _name##_hw __initdata = { \ + .ops = _hwops, \ + .enable_reg = { .reginfo = { _enable_reg }},\ + .enable_bit = _enable_bit, \ + .clksel = { .init = _clksel }, \ + .clksel_reg = { .reginfo = { _clksel_reg }},\ + .clksel_mask = _clksel_mask, \ + .clkdm_name = _clkdm_name, \ + }; \ + OMAP_CLK(_name, _parent_names, _ops); + +#define DEFINE_CLK_OMAP_HSDIVIDER63(_name, _parent_name, \ + _parent_ptr, _flags, \ + _clksel_module, _clksel_offset, \ + _clksel_mask) \ + \ + _DEFINE_CLK_OMAP_HSDIVIDER(_name, _parent_name, \ + _parent_ptr, _flags, \ + _clksel_module, _clksel_offset, \ + _clksel_mask, 63) + +#define DEFINE_CLK_OMAP_HSDIVIDER(_name, _parent_name, \ + _parent_ptr, _flags, \ + _clksel_reg, _clksel_mask) \ + \ + _DEFINE_CLK_OMAP_HSDIVIDER(_name, _parent_name, \ + _parent_ptr, _flags, \ + _clksel_reg, _clksel_mask, 31) + +#define _DEFINE_CLK_OMAP_HSDIVIDER(_name, _parent_name, \ + _ignore, _flags, \ + _module, _offset, \ + _clksel_mask, mdiv) \ + static struct clksel_init _name##_div[] __initdata = { \ + { \ + .rates = div##mdiv##_1to##mdiv##_rates, \ + .parent_name = _parent_name, \ + }, \ + { .parent_name = NULL }, \ + }; \ + static const char *_name##_parent_names[] __initdata = {\ + _parent_name, \ + }; \ + static struct clk_hw_omap _name##_hw __initdata = { \ + .clksel = { .init = _name##_div }, \ + .clksel_reg = { \ + .reginfo = { \ + .module = _module, \ + .offset = _offset }, \ + }, \ + .clksel_mask = _clksel_mask, \ + .ops = &clkhwops_omap4_dpllmx, \ + }; \ + OMAP_CLK(_name, _name##_parent_names, omap_hsdivider_ops); + +/* struct clksel_rate.flags possibilities */ +#define RATE_IN_242X (1 << 0) +#define RATE_IN_243X (1 << 1) +#define RATE_IN_3430ES1 (1 << 2) /* 3430ES1 rates only */ +#define RATE_IN_3430ES2PLUS (1 << 3) /* 3430 ES >= 2 rates only */ +#define RATE_IN_36XX (1 << 4) +#define RATE_IN_4430 (1 << 5) +#define RATE_IN_TI816X (1 << 6) +#define RATE_IN_4460 (1 << 7) +#define RATE_IN_AM33XX (1 << 8) +#define RATE_IN_TI814X (1 << 9) +#define RATE_IN_54XX (1 << 10) + +#define RATE_IN_24XX (RATE_IN_242X | RATE_IN_243X) +#define RATE_IN_34XX (RATE_IN_3430ES1 | RATE_IN_3430ES2PLUS) +#define RATE_IN_3XXX (RATE_IN_34XX | RATE_IN_36XX) +#define RATE_IN_44XX (RATE_IN_4430 | RATE_IN_4460) + +/* RATE_IN_3430ES2PLUS_36XX includes 34xx/35xx with ES >=2, and all 36xx/37xx */ +#define RATE_IN_3430ES2PLUS_36XX (RATE_IN_3430ES2PLUS | RATE_IN_36XX) + + +/** + * struct clksel_rate - register bitfield values corresponding to clk divisors + * @val: register bitfield value (shifted to bit 0) + * @div: clock divisor corresponding to @val + * @flags: (see "struct clksel_rate.flags possibilities" above) + * + * @val should match the value of a read from struct clk.clksel_reg + * AND'ed with struct clk.clksel_mask, shifted right to bit 0. + * + * @div is the divisor that should be applied to the parent clock's rate + * to produce the current clock's rate. + */ +struct clksel_rate { + u32 val; + u8 div; + u16 flags; +}; + +/** + * struct clksel - available parent clocks, and a pointer to their divisors + * @parent: struct clk * to a possible parent clock + * @rates: available divisors for this parent clock + * + * A struct clksel is always associated with one or more struct clks + * and one or more struct clksel_rates. + */ +struct clksel { + struct clk *parent; + const struct clksel_rate *rates; +}; + +struct clksel_init { + struct clksel *clksel; + const struct clksel_rate *rates; + const char *parent_name; +}; + +/** + * struct dpll_data - DPLL registers and integration data + * @mult_div1_reg: register containing the DPLL M and N bitfields + * @mult_mask: mask of the DPLL M bitfield in @mult_div1_reg + * @div1_mask: mask of the DPLL N bitfield in @mult_div1_reg + * @clk_bypass: struct clk pointer to the clock's bypass clock input + * @clk_ref: struct clk pointer to the clock's reference clock input + * @control_reg: register containing the DPLL mode bitfield + * @enable_mask: mask of the DPLL mode bitfield in @control_reg + * @last_rounded_rate: cache of the last rate result of omap2_dpll_round_rate() + * @last_rounded_m: cache of the last M result of omap2_dpll_round_rate() + * @last_rounded_m4xen: cache of the last M4X result of + * omap4_dpll_regm4xen_round_rate() + * @last_rounded_lpmode: cache of the last lpmode result of + * omap4_dpll_lpmode_recalc() + * @max_multiplier: maximum valid non-bypass multiplier value (actual) + * @last_rounded_n: cache of the last N result of omap2_dpll_round_rate() + * @min_divider: minimum valid non-bypass divider value (actual) + * @max_divider: maximum valid non-bypass divider value (actual) + * @modes: possible values of @enable_mask + * @autoidle_reg: register containing the DPLL autoidle mode bitfield + * @idlest_reg: register containing the DPLL idle status bitfield + * @autoidle_mask: mask of the DPLL autoidle mode bitfield in @autoidle_reg + * @freqsel_mask: mask of the DPLL jitter correction bitfield in @control_reg + * @idlest_mask: mask of the DPLL idle status bitfield in @idlest_reg + * @lpmode_mask: mask of the DPLL low-power mode bitfield in @control_reg + * @m4xen_mask: mask of the DPLL M4X multiplier bitfield in @control_reg + * @auto_recal_bit: bitshift of the driftguard enable bit in @control_reg + * @recal_en_bit: bitshift of the PRM_IRQENABLE_* bit for recalibration IRQs + * @recal_st_bit: bitshift of the PRM_IRQSTATUS_* bit for recalibration IRQs + * @flags: DPLL type/features (see below) + * + * Possible values for @flags: + * DPLL_J_TYPE: "J-type DPLL" (only some 36xx, 4xxx DPLLs) + * + * @freqsel_mask is only used on the OMAP34xx family and AM35xx. + * + * XXX Some DPLLs have multiple bypass inputs, so it's not technically + * correct to only have one @clk_bypass pointer. + * + * XXX The runtime-variable fields (@last_rounded_rate, @last_rounded_m, + * @last_rounded_n) should be separated from the runtime-fixed fields + * and placed into a different structure, so that the runtime-fixed data + * can be placed into read-only space. + */ +struct dpll_data { + union { + void __iomem *ptr; + struct clk_reginfo reginfo; + } mult_div1_reg; + u32 mult_mask; + u32 div1_mask; + union { + struct clk *clk; + const char *name; + } clk_bypass; + union { + struct clk *clk; + const char *name; + } clk_ref; + union { + void __iomem *ptr; + struct clk_reginfo reginfo; + } control_reg; + u32 enable_mask; + unsigned long last_rounded_rate; + u16 last_rounded_m; + u8 last_rounded_m4xen; + u8 last_rounded_lpmode; + u16 max_multiplier; + u8 last_rounded_n; + u8 min_divider; + u16 max_divider; + u8 modes; + union { + void __iomem *ptr; + struct clk_reginfo reginfo; + } autoidle_reg; + union { + void __iomem *ptr; + struct clk_reginfo reginfo; + } idlest_reg; + u32 autoidle_mask; + u32 freqsel_mask; + u32 idlest_mask; + u32 dco_mask; + u32 sddiv_mask; + u32 lpmode_mask; + u32 m4xen_mask; + u8 auto_recal_bit; + u8 recal_en_bit; + u8 recal_st_bit; + u8 flags; +}; + +/* + * struct clk.flags possibilities + * + * XXX document the rest of the clock flags here + * + * CLOCK_CLKOUTX2: (OMAP4 only) DPLL CLKOUT and CLKOUTX2 GATE_CTRL + * bits share the same register. This flag allows the + * omap4_dpllmx*() code to determine which GATE_CTRL bit field + * should be used. This is a temporary solution - a better approach + * would be to associate clock type-specific data with the clock, + * similar to the struct dpll_data approach. + */ +#define ENABLE_REG_32BIT (1 << 0) /* Use 32-bit access */ +#define CLOCK_IDLE_CONTROL (1 << 1) +#define CLOCK_NO_IDLE_PARENT (1 << 2) +#define ENABLE_ON_INIT (1 << 3) /* Enable upon framework init */ +#define INVERT_ENABLE (1 << 4) /* 0 enables, 1 disables */ +#define CLOCK_CLKOUTX2 (1 << 5) + +/** + * struct clk_hw_omap - OMAP struct clk + * @node: list_head connecting this clock into the full clock list + * @enable_reg: register to write to enable the clock (see @enable_bit) + * @enable_bit: bitshift to write to enable/disable the clock (see @enable_reg) + * @flags: see "struct clk.flags possibilities" above + * @clksel_reg: for clksel clks, register va containing src/divisor select + * @clksel_mask: bitmask in @clksel_reg for the src/divisor selector + * @clksel: for clksel clks, pointer to struct clksel for this clock + * @dpll_data: for DPLLs, pointer to struct dpll_data for this clock + * @clkdm_name: clockdomain name that this clock is contained in + * @clkdm: pointer to struct clockdomain, resolved from @clkdm_name at runtime + * @rate_offset: bitshift for rate selection bitfield (OMAP1 only) + * @src_offset: bitshift for source selection bitfield (OMAP1 only) + * + * XXX @rate_offset, @src_offset should probably be removed and OMAP1 + * clock code converted to use clksel. + * + */ + +struct clk_hw_omap_ops; + +struct clk_hw_omap { + struct clk_hw hw; + struct list_head node; + unsigned long fixed_rate; + u8 fixed_div; + union { + void __iomem *ptr; + struct clk_reginfo reginfo; + } enable_reg; + u8 enable_bit; + u8 flags; + union { + void __iomem *ptr; + struct clk_reginfo reginfo; + } clksel_reg; + u32 clksel_mask; + union { + struct clksel *ptr; + struct clksel_init *init; + } clksel; + struct dpll_data *dpll_data; + const char *clkdm_name; + struct clockdomain *clkdm; + const struct clk_hw_omap_ops *ops; +}; + +struct clk_hw_omap_ops { + void (*find_idlest)(struct clk_hw_omap *oclk, + void __iomem **idlest_reg, + u8 *idlest_bit, u8 *idlest_val); + void (*find_companion)(struct clk_hw_omap *oclk, + void __iomem **other_reg, + u8 *other_bit); + void (*allow_idle)(struct clk_hw_omap *oclk); + void (*deny_idle)(struct clk_hw_omap *oclk); +}; + +unsigned long omap_fixed_divisor_recalc(struct clk_hw *hw, + unsigned long parent_rate); + +/* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */ +#define CORE_CLK_SRC_32K 0x0 +#define CORE_CLK_SRC_DPLL 0x1 +#define CORE_CLK_SRC_DPLL_X2 0x2 + +/* OMAP2xxx CM_CLKEN_PLL.EN_DPLL bits - for omap2_get_dpll_rate() */ +#define OMAP2XXX_EN_DPLL_LPBYPASS 0x1 +#define OMAP2XXX_EN_DPLL_FRBYPASS 0x2 +#define OMAP2XXX_EN_DPLL_LOCKED 0x3 + +/* OMAP3xxx CM_CLKEN_PLL*.EN_*_DPLL bits - for omap2_get_dpll_rate() */ +#define OMAP3XXX_EN_DPLL_LPBYPASS 0x5 +#define OMAP3XXX_EN_DPLL_FRBYPASS 0x6 +#define OMAP3XXX_EN_DPLL_LOCKED 0x7 + +/* OMAP4xxx CM_CLKMODE_DPLL*.EN_*_DPLL bits - for omap2_get_dpll_rate() */ +#define OMAP4XXX_EN_DPLL_MNBYPASS 0x4 +#define OMAP4XXX_EN_DPLL_LPBYPASS 0x5 +#define OMAP4XXX_EN_DPLL_FRBYPASS 0x6 +#define OMAP4XXX_EN_DPLL_LOCKED 0x7 + +/* CM_CLKEN_PLL*.EN* bit values - not all are available for every DPLL */ +#define DPLL_LOW_POWER_STOP 0x1 +#define DPLL_LOW_POWER_BYPASS 0x5 +#define DPLL_LOCKED 0x7 + +/* DPLL Type and DCO Selection Flags */ +#define DPLL_J_TYPE 0x1 + +long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, + unsigned long *parent_rate); +unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate); +int omap3_noncore_dpll_enable(struct clk_hw *hw); +void omap3_noncore_dpll_disable(struct clk_hw *hw); +int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk); +void omap3_dpll_allow_idle(struct clk_hw_omap *clk); +void omap3_dpll_deny_idle(struct clk_hw_omap *clk); +unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, + unsigned long parent_rate); +int omap4_dpllmx_gatectrl_read(struct clk_hw_omap *clk); +void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk); +void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk); +unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, + unsigned long parent_rate); +long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, + unsigned long target_rate, + unsigned long *parent_rate); + +void omap2_init_clk_clkdm(struct clk_hw *clk); +void __init omap2_clk_disable_clkdm_control(void); + +/* clkt_clksel.c public functions */ +u32 omap2_clksel_round_rate_div(struct clk_hw_omap *clk, + unsigned long target_rate, + u32 *new_div); +u8 omap2_clksel_find_parent_index(struct clk_hw *hw); +unsigned long omap2_clksel_recalc(struct clk_hw *hw, unsigned long parent_rate); +long omap2_clksel_round_rate(struct clk_hw *hw, unsigned long target_rate, + unsigned long *parent_rate); +int omap2_clksel_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +int omap2_clksel_set_parent(struct clk_hw *hw, u8 field_val); + +/* clkt_iclk.c public functions */ +extern void omap2_clkt_iclk_allow_idle(struct clk_hw_omap *clk); +extern void omap2_clkt_iclk_deny_idle(struct clk_hw_omap *clk); + +u8 omap2_init_dpll_parent(struct clk_hw *hw); +unsigned long omap2_get_dpll_rate(struct clk_hw_omap *clk); + +int omap2_dflt_clk_enable(struct clk_hw *hw); +void omap2_dflt_clk_disable(struct clk_hw *hw); +int omap2_dflt_clk_is_enabled(struct clk_hw *hw); +void omap2_clk_dflt_find_companion(struct clk_hw_omap *clk, + void __iomem **other_reg, + u8 *other_bit); +void omap2_clk_dflt_find_idlest(struct clk_hw_omap *clk, + void __iomem **idlest_reg, + u8 *idlest_bit, u8 *idlest_val); +void omap2_init_clk_hw_omap_clocks(struct clk *clk); +int omap2_clk_enable_autoidle_all(void); +int omap2_clk_disable_autoidle_all(void); +void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks); +int omap2_clk_switch_mpurate_at_boot(const char *mpurate_ck_name); +void omap2_clk_print_new_rates(const char *hfclkin_ck_name, + const char *core_ck_name, + const char *mpu_ck_name); + +extern u16 cpu_mask; + +extern const struct clkops clkops_omap2_dflt_wait; +extern const struct clkops clkops_dummy; +extern const struct clkops clkops_omap2_dflt; + +extern struct clk_functions omap2_clk_functions; + +extern const struct clksel_rate gpt_32k_rates[]; +extern const struct clksel_rate gpt_sys_rates[]; +extern const struct clksel_rate gfx_l3_rates[]; +extern const struct clksel_rate dsp_ick_rates[]; + +extern struct omap_init_clk omap_dummy_ck; + +extern const struct clk_hw_omap_ops clkhwops_omap3_dpll; +extern const struct clk_hw_omap_ops clkhwops_iclk_wait; +extern const struct clk_hw_omap_ops clkhwops_wait; +extern const struct clk_hw_omap_ops clkhwops_omap4_dpllmx; +extern const struct clk_hw_omap_ops clkhwops_iclk; +extern const struct clk_hw_omap_ops clkhwops_omap3430es2_ssi_wait; +extern const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_ssi_wait; +extern const struct clk_hw_omap_ops clkhwops_omap3430es2_dss_usbhost_wait; +extern const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_dss_usbhost_wait; +extern const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_hsotgusb_wait; +extern const struct clk_hw_omap_ops clkhwops_omap3430es2_hsotgusb_wait; +extern const struct clk_hw_omap_ops clkhwops_am35xx_ipss_module_wait; +extern const struct clk_hw_omap_ops clkhwops_am35xx_ipss_wait; +extern const struct clk_hw_omap_ops clkhwops_apll54; +extern const struct clk_hw_omap_ops clkhwops_apll96; +extern const struct clk_hw_omap_ops clkhwops_omap2xxx_dpll; +extern const struct clk_hw_omap_ops clkhwops_omap2430_i2chs_wait; + +/* clksel_rate blocks shared between OMAP44xx and AM33xx */ +extern const struct clksel_rate div_1_0_rates[]; +extern const struct clksel_rate div3_1to4_rates[]; +extern const struct clksel_rate div_1_1_rates[]; +extern const struct clksel_rate div_1_2_rates[]; +extern const struct clksel_rate div_1_3_rates[]; +extern const struct clksel_rate div_1_4_rates[]; +extern const struct clksel_rate div31_1to31_rates[]; +extern const struct clksel_rate div63_1to63_rates[]; + +extern int am33xx_clk_init(void); + +extern int omap2_clkops_enable_clkdm(struct clk_hw *hw); +extern void omap2_clkops_disable_clkdm(struct clk_hw *hw); + +#ifdef CONFIG_ARCH_OMAP4 +extern void omap4xxx_clocks_init(struct device_node *np); +#else +static inline void omap4xxx_clocks_init(struct device_node *np) {} +#endif /* CONFIG_ARCH_OMAP4 */ + +extern int omap_clk_register_clks(struct omap_clk *clks, int size, + u32 cpu_clkflg, void __iomem **base); + +extern struct clk *omap_clk_register(struct omap_init_clk *c); +extern struct clk *omap_clk_register_fixed_rate(struct omap_init_clk *c); +extern struct clk *omap_clk_register_mux(struct omap_init_clk *c); +extern struct clk *omap_clk_register_gate(struct omap_init_clk *c); +extern struct clk *omap_clk_register_divider(struct omap_init_clk *c); +extern struct clk *omap_clk_register_fixed_factor(struct omap_init_clk *c); + +#endif