···11-/*22- * clkt_clksel.c - OMAP2/3/4 clksel clock functions33- *44- * Copyright (C) 2005-2008 Texas Instruments, Inc.55- * Copyright (C) 2004-2010 Nokia Corporation66- *77- * Contacts:88- * Richard Woodruff <r-woodruff2@ti.com>99- * Paul Walmsley1010- *1111- * This program is free software; you can redistribute it and/or modify1212- * it under the terms of the GNU General Public License version 2 as1313- * published by the Free Software Foundation.1414- *1515- *1616- * clksel clocks are clocks that do not have a fixed parent, or that1717- * can divide their parent's rate, or possibly both at the same time, based1818- * on the contents of a hardware register bitfield.1919- *2020- * All of the various mux and divider settings can be encoded into2121- * struct clksel* data structures, and then these can be autogenerated2222- * from some hardware database for each new chip generation. This2323- * should avoid the need to write, review, and validate a lot of new2424- * clock code for each new chip, since it can be exported from the SoC2525- * design flow. This is now done on OMAP4.2626- *2727- * The fusion of mux and divider clocks is a software creation. In2828- * hardware reality, the multiplexer (parent selection) and the2929- * divider exist separately. XXX At some point these clksel clocks3030- * should be split into "divider" clocks and "mux" clocks to better3131- * match the hardware.3232- *3333- * (The name "clksel" comes from the name of the corresponding3434- * register field in the OMAP2/3 family of SoCs.)3535- *3636- * XXX Currently these clocks are only used in the OMAP2/3/4 code, but3737- * many of the OMAP1 clocks should be convertible to use this3838- * mechanism.3939- */4040-#undef DEBUG4141-4242-#include <linux/kernel.h>4343-#include <linux/errno.h>4444-#include <linux/clk-provider.h>4545-#include <linux/io.h>4646-#include <linux/bug.h>4747-4848-#include "clock.h"4949-5050-/* Private functions */5151-5252-/**5353- * _get_clksel_by_parent() - return clksel struct for a given clk & parent5454- * @clk: OMAP struct clk ptr to inspect5555- * @src_clk: OMAP struct clk ptr of the parent clk to search for5656- *5757- * Scan the struct clksel array associated with the clock to find5858- * the element associated with the supplied parent clock address.5959- * Returns a pointer to the struct clksel on success or NULL on error.6060- */6161-static const struct clksel *_get_clksel_by_parent(struct clk_hw_omap *clk,6262- struct clk *src_clk)6363-{6464- const struct clksel *clks;6565-6666- if (!src_clk)6767- return NULL;6868-6969- for (clks = clk->clksel; clks->parent; clks++)7070- if (clks->parent == src_clk)7171- break; /* Found the requested parent */7272-7373- if (!clks->parent) {7474- /* This indicates a data problem */7575- WARN(1, "clock: %s: could not find parent clock %s in clksel array\n",7676- __clk_get_name(clk->hw.clk), __clk_get_name(src_clk));7777- return NULL;7878- }7979-8080- return clks;8181-}8282-8383-/**8484- * _write_clksel_reg() - program a clock's clksel register in hardware8585- * @clk: struct clk * to program8686- * @v: clksel bitfield value to program (with LSB at bit 0)8787- *8888- * Shift the clksel register bitfield value @v to its appropriate8989- * location in the clksel register and write it in. This function9090- * will ensure that the write to the clksel_reg reaches its9191- * destination before returning -- important since PRM and CM register9292- * accesses can be quite slow compared to ARM cycles -- but does not9393- * take into account any time the hardware might take to switch the9494- * clock source.9595- */9696-static void _write_clksel_reg(struct clk_hw_omap *clk, u32 field_val)9797-{9898- u32 v;9999-100100- v = omap2_clk_readl(clk, clk->clksel_reg);101101- v &= ~clk->clksel_mask;102102- v |= field_val << __ffs(clk->clksel_mask);103103- omap2_clk_writel(v, clk, clk->clksel_reg);104104-105105- v = omap2_clk_readl(clk, clk->clksel_reg); /* OCP barrier */106106-}107107-108108-/**109109- * _clksel_to_divisor() - turn clksel field value into integer divider110110- * @clk: OMAP struct clk to use111111- * @field_val: register field value to find112112- *113113- * Given a struct clk of a rate-selectable clksel clock, and a register field114114- * value to search for, find the corresponding clock divisor. The register115115- * field value should be pre-masked and shifted down so the LSB is at bit 0116116- * before calling. Returns 0 on error or returns the actual integer divisor117117- * upon success.118118- */119119-static u32 _clksel_to_divisor(struct clk_hw_omap *clk, u32 field_val)120120-{121121- const struct clksel *clks;122122- const struct clksel_rate *clkr;123123- struct clk *parent;124124-125125- parent = __clk_get_parent(clk->hw.clk);126126-127127- clks = _get_clksel_by_parent(clk, parent);128128- if (!clks)129129- return 0;130130-131131- for (clkr = clks->rates; clkr->div; clkr++) {132132- if (!(clkr->flags & cpu_mask))133133- continue;134134-135135- if (clkr->val == field_val)136136- break;137137- }138138-139139- if (!clkr->div) {140140- /* This indicates a data error */141141- WARN(1, "clock: %s: could not find fieldval %d for parent %s\n",142142- __clk_get_name(clk->hw.clk), field_val,143143- __clk_get_name(parent));144144- return 0;145145- }146146-147147- return clkr->div;148148-}149149-150150-/**151151- * _divisor_to_clksel() - turn clksel integer divisor into a field value152152- * @clk: OMAP struct clk to use153153- * @div: integer divisor to search for154154- *155155- * Given a struct clk of a rate-selectable clksel clock, and a clock156156- * divisor, find the corresponding register field value. Returns the157157- * register field value _before_ left-shifting (i.e., LSB is at bit158158- * 0); or returns 0xFFFFFFFF (~0) upon error.159159- */160160-static u32 _divisor_to_clksel(struct clk_hw_omap *clk, u32 div)161161-{162162- const struct clksel *clks;163163- const struct clksel_rate *clkr;164164- struct clk *parent;165165-166166- /* should never happen */167167- WARN_ON(div == 0);168168-169169- parent = __clk_get_parent(clk->hw.clk);170170- clks = _get_clksel_by_parent(clk, parent);171171- if (!clks)172172- return ~0;173173-174174- for (clkr = clks->rates; clkr->div; clkr++) {175175- if (!(clkr->flags & cpu_mask))176176- continue;177177-178178- if (clkr->div == div)179179- break;180180- }181181-182182- if (!clkr->div) {183183- pr_err("clock: %s: could not find divisor %d for parent %s\n",184184- __clk_get_name(clk->hw.clk), div,185185- __clk_get_name(parent));186186- return ~0;187187- }188188-189189- return clkr->val;190190-}191191-192192-/**193193- * _read_divisor() - get current divisor applied to parent clock (from hdwr)194194- * @clk: OMAP struct clk to use.195195- *196196- * Read the current divisor register value for @clk that is programmed197197- * into the hardware, convert it into the actual divisor value, and198198- * return it; or return 0 on error.199199- */200200-static u32 _read_divisor(struct clk_hw_omap *clk)201201-{202202- u32 v;203203-204204- if (!clk->clksel || !clk->clksel_mask)205205- return 0;206206-207207- v = omap2_clk_readl(clk, clk->clksel_reg);208208- v &= clk->clksel_mask;209209- v >>= __ffs(clk->clksel_mask);210210-211211- return _clksel_to_divisor(clk, v);212212-}213213-214214-/* Public functions */215215-216216-/**217217- * omap2_clksel_round_rate_div() - find divisor for the given clock and rate218218- * @clk: OMAP struct clk to use219219- * @target_rate: desired clock rate220220- * @new_div: ptr to where we should store the divisor221221- *222222- * Finds 'best' divider value in an array based on the source and target223223- * rates. The divider array must be sorted with smallest divider first.224224- * This function is also used by the DPLL3 M2 divider code.225225- *226226- * Returns the rounded clock rate or returns 0xffffffff on error.227227- */228228-u32 omap2_clksel_round_rate_div(struct clk_hw_omap *clk,229229- unsigned long target_rate,230230- u32 *new_div)231231-{232232- unsigned long test_rate;233233- const struct clksel *clks;234234- const struct clksel_rate *clkr;235235- u32 last_div = 0;236236- struct clk *parent;237237- unsigned long parent_rate;238238- const char *clk_name;239239-240240- parent = __clk_get_parent(clk->hw.clk);241241- clk_name = __clk_get_name(clk->hw.clk);242242- parent_rate = __clk_get_rate(parent);243243-244244- if (!clk->clksel || !clk->clksel_mask)245245- return ~0;246246-247247- pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n",248248- clk_name, target_rate);249249-250250- *new_div = 1;251251-252252- clks = _get_clksel_by_parent(clk, parent);253253- if (!clks)254254- return ~0;255255-256256- for (clkr = clks->rates; clkr->div; clkr++) {257257- if (!(clkr->flags & cpu_mask))258258- continue;259259-260260- /* Sanity check */261261- if (clkr->div <= last_div)262262- pr_err("clock: %s: clksel_rate table not sorted\n",263263- clk_name);264264-265265- last_div = clkr->div;266266-267267- test_rate = parent_rate / clkr->div;268268-269269- if (test_rate <= target_rate)270270- break; /* found it */271271- }272272-273273- if (!clkr->div) {274274- pr_err("clock: %s: could not find divisor for target rate %ld for parent %s\n",275275- clk_name, target_rate, __clk_get_name(parent));276276- return ~0;277277- }278278-279279- *new_div = clkr->div;280280-281281- pr_debug("clock: new_div = %d, new_rate = %ld\n", *new_div,282282- (parent_rate / clkr->div));283283-284284- return parent_rate / clkr->div;285285-}286286-287287-/*288288- * Clocktype interface functions to the OMAP clock code289289- * (i.e., those used in struct clk field function pointers, etc.)290290- */291291-292292-/**293293- * omap2_clksel_find_parent_index() - return the array index of the current294294- * hardware parent of @hw295295- * @hw: struct clk_hw * to find the current hardware parent of296296- *297297- * Given a struct clk_hw pointer @hw to the 'hw' member of a struct298298- * clk_hw_omap record representing a source-selectable hardware clock,299299- * read the hardware register and determine what its parent is300300- * currently set to. Intended to be called only by the common clock301301- * framework struct clk_hw_ops.get_parent function pointer. Return302302- * the array index of this parent clock upon success -- there is no303303- * way to return an error, so if we encounter an error, just WARN()304304- * and pretend that we know that we're doing.305305- */306306-u8 omap2_clksel_find_parent_index(struct clk_hw *hw)307307-{308308- struct clk_hw_omap *clk = to_clk_hw_omap(hw);309309- const struct clksel *clks;310310- const struct clksel_rate *clkr;311311- u32 r, found = 0;312312- struct clk *parent;313313- const char *clk_name;314314- int ret = 0, f = 0;315315-316316- parent = __clk_get_parent(hw->clk);317317- clk_name = __clk_get_name(hw->clk);318318-319319- /* XXX should be able to return an error */320320- WARN((!clk->clksel || !clk->clksel_mask),321321- "clock: %s: attempt to call on a non-clksel clock", clk_name);322322-323323- r = omap2_clk_readl(clk, clk->clksel_reg) & clk->clksel_mask;324324- r >>= __ffs(clk->clksel_mask);325325-326326- for (clks = clk->clksel; clks->parent && !found; clks++) {327327- for (clkr = clks->rates; clkr->div && !found; clkr++) {328328- if (!(clkr->flags & cpu_mask))329329- continue;330330-331331- if (clkr->val == r) {332332- found = 1;333333- ret = f;334334- }335335- }336336- f++;337337- }338338-339339- /* This indicates a data error */340340- WARN(!found, "clock: %s: init parent: could not find regval %0x\n",341341- clk_name, r);342342-343343- return ret;344344-}345345-346346-347347-/**348348- * omap2_clksel_recalc() - function ptr to pass via struct clk .recalc field349349- * @clk: struct clk *350350- *351351- * This function is intended to be called only by the clock framework.352352- * Each clksel clock should have its struct clk .recalc field set to this353353- * function. Returns the clock's current rate, based on its parent's rate354354- * and its current divisor setting in the hardware.355355- */356356-unsigned long omap2_clksel_recalc(struct clk_hw *hw, unsigned long parent_rate)357357-{358358- unsigned long rate;359359- u32 div = 0;360360- struct clk_hw_omap *clk = to_clk_hw_omap(hw);361361-362362- if (!parent_rate)363363- return 0;364364-365365- div = _read_divisor(clk);366366- if (!div)367367- rate = parent_rate;368368- else369369- rate = parent_rate / div;370370-371371- pr_debug("%s: recalc'd %s's rate to %lu (div %d)\n", __func__,372372- __clk_get_name(hw->clk), rate, div);373373-374374- return rate;375375-}376376-377377-/**378378- * omap2_clksel_round_rate() - find rounded rate for the given clock and rate379379- * @clk: OMAP struct clk to use380380- * @target_rate: desired clock rate381381- *382382- * This function is intended to be called only by the clock framework.383383- * Finds best target rate based on the source clock and possible dividers.384384- * rates. The divider array must be sorted with smallest divider first.385385- *386386- * Returns the rounded clock rate or returns 0xffffffff on error.387387- */388388-long omap2_clksel_round_rate(struct clk_hw *hw, unsigned long target_rate,389389- unsigned long *parent_rate)390390-{391391- struct clk_hw_omap *clk = to_clk_hw_omap(hw);392392- u32 new_div;393393-394394- return omap2_clksel_round_rate_div(clk, target_rate, &new_div);395395-}396396-397397-/**398398- * omap2_clksel_set_rate() - program clock rate in hardware399399- * @clk: struct clk * to program rate400400- * @rate: target rate to program401401- *402402- * This function is intended to be called only by the clock framework.403403- * Program @clk's rate to @rate in the hardware. The clock can be404404- * either enabled or disabled when this happens, although if the clock405405- * is enabled, some downstream devices may glitch or behave406406- * unpredictably when the clock rate is changed - this depends on the407407- * hardware. This function does not currently check the usecount of408408- * the clock, so if multiple drivers are using the clock, and the rate409409- * is changed, they will all be affected without any notification.410410- * Returns -EINVAL upon error, or 0 upon success.411411- */412412-int omap2_clksel_set_rate(struct clk_hw *hw, unsigned long rate,413413- unsigned long parent_rate)414414-{415415- struct clk_hw_omap *clk = to_clk_hw_omap(hw);416416- u32 field_val, validrate, new_div = 0;417417-418418- if (!clk->clksel || !clk->clksel_mask)419419- return -EINVAL;420420-421421- validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);422422- if (validrate != rate)423423- return -EINVAL;424424-425425- field_val = _divisor_to_clksel(clk, new_div);426426- if (field_val == ~0)427427- return -EINVAL;428428-429429- _write_clksel_reg(clk, field_val);430430-431431- pr_debug("clock: %s: set rate to %ld\n", __clk_get_name(hw->clk),432432- __clk_get_rate(hw->clk));433433-434434- return 0;435435-}436436-437437-/*438438- * Clksel parent setting function - not passed in struct clk function439439- * pointer - instead, the OMAP clock code currently assumes that any440440- * parent-setting clock is a clksel clock, and calls441441- * omap2_clksel_set_parent() by default442442- */443443-444444-/**445445- * omap2_clksel_set_parent() - change a clock's parent clock446446- * @clk: struct clk * of the child clock447447- * @new_parent: struct clk * of the new parent clock448448- *449449- * This function is intended to be called only by the clock framework.450450- * Change the parent clock of clock @clk to @new_parent. This is451451- * intended to be used while @clk is disabled. This function does not452452- * currently check the usecount of the clock, so if multiple drivers453453- * are using the clock, and the parent is changed, they will all be454454- * affected without any notification. Returns -EINVAL upon error, or455455- * 0 upon success.456456- */457457-int omap2_clksel_set_parent(struct clk_hw *hw, u8 field_val)458458-{459459- struct clk_hw_omap *clk = to_clk_hw_omap(hw);460460-461461- if (!clk->clksel || !clk->clksel_mask)462462- return -EINVAL;463463-464464- _write_clksel_reg(clk, field_val);465465- return 0;466466-}
-12
arch/arm/mach-omap2/clock.h
···186186187187void __init omap2_clk_disable_clkdm_control(void);188188189189-/* clkt_clksel.c public functions */190190-u32 omap2_clksel_round_rate_div(struct clk_hw_omap *clk,191191- unsigned long target_rate,192192- u32 *new_div);193193-u8 omap2_clksel_find_parent_index(struct clk_hw *hw);194194-unsigned long omap2_clksel_recalc(struct clk_hw *hw, unsigned long parent_rate);195195-long omap2_clksel_round_rate(struct clk_hw *hw, unsigned long target_rate,196196- unsigned long *parent_rate);197197-int omap2_clksel_set_rate(struct clk_hw *hw, unsigned long rate,198198- unsigned long parent_rate);199199-int omap2_clksel_set_parent(struct clk_hw *hw, u8 field_val);200200-201189int omap2_clk_enable_autoidle_all(void);202190int omap2_clk_allow_idle(struct clk *clk);203191int omap2_clk_deny_idle(struct clk *clk);