|
@@ -0,0 +1,92 @@
|
|
|
+/*
|
|
|
+ * clkt_clksel.c - OMAP2/3/4 clksel clock functions
|
|
|
+ *
|
|
|
+ * Copyright (C) 2005-2008 Texas Instruments, Inc.
|
|
|
+ * Copyright (C) 2004-2010 Nokia Corporation
|
|
|
+ *
|
|
|
+ * Contacts:
|
|
|
+ * Richard Woodruff <r-woodruff2@ti.com>
|
|
|
+ * 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.
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * clksel clocks are clocks that do not have a fixed parent, or that
|
|
|
+ * can divide their parent's rate, or possibly both at the same time, based
|
|
|
+ * on the contents of a hardware register bitfield.
|
|
|
+ *
|
|
|
+ * All of the various mux and divider settings can be encoded into
|
|
|
+ * struct clksel* data structures, and then these can be autogenerated
|
|
|
+ * from some hardware database for each new chip generation. This
|
|
|
+ * should avoid the need to write, review, and validate a lot of new
|
|
|
+ * clock code for each new chip, since it can be exported from the SoC
|
|
|
+ * design flow. This is now done on OMAP4.
|
|
|
+ *
|
|
|
+ * The fusion of mux and divider clocks is a software creation. In
|
|
|
+ * hardware reality, the multiplexer (parent selection) and the
|
|
|
+ * divider exist separately. XXX At some point these clksel clocks
|
|
|
+ * should be split into "divider" clocks and "mux" clocks to better
|
|
|
+ * match the hardware.
|
|
|
+ *
|
|
|
+ * (The name "clksel" comes from the name of the corresponding
|
|
|
+ * register field in the OMAP2/3 family of SoCs.)
|
|
|
+ *
|
|
|
+ * XXX Currently these clocks are only used in the OMAP2/3/4 code, but
|
|
|
+ * many of the OMAP1 clocks should be convertible to use this
|
|
|
+ * mechanism.
|
|
|
+ */
|
|
|
+#undef DEBUG
|
|
|
+
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/errno.h>
|
|
|
+#include <linux/clk-provider.h>
|
|
|
+#include <linux/io.h>
|
|
|
+#include <linux/bug.h>
|
|
|
+
|
|
|
+#include "clock.h"
|
|
|
+
|
|
|
+/* Private functions */
|
|
|
+
|
|
|
+/**
|
|
|
+ * _get_clksel_by_parent() - return clksel struct for a given clk & parent
|
|
|
+ * @clk: OMAP struct clk ptr to inspect
|
|
|
+ * @src_clk: OMAP struct clk ptr of the parent clk to search for
|
|
|
+ *
|
|
|
+ * Scan the struct clksel array associated with the clock to find
|
|
|
+ * the element associated with the supplied parent clock address.
|
|
|
+ * Returns a pointer to the struct clksel on success or NULL on error.
|
|
|
+ */
|
|
|
+static const struct clksel *_get_clksel_by_parent(struct clk_hw_omap *clk,
|
|
|
+ struct clk *src_clk)
|
|
|
+{
|
|
|
+ const struct clksel *clks;
|
|
|
+
|
|
|
+ if (!src_clk)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ for (clks = clk->clksel; clks->parent; clks++)
|
|
|
+ if (clks->parent == src_clk)
|
|
|
+ break; /* Found the requested parent */
|
|
|
+
|
|
|
+ if (!clks->parent) {
|
|
|
+ /* This indicates a data problem */
|
|
|
+ WARN(1, "clock: %s: could not find parent clock %s in clksel array\n",
|
|
|
+ __clk_get_name(clk->hw.clk), __clk_get_name(src_clk));
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return clks;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _write_clksel_reg() - program a clock's clksel register in hardware
|
|
|
+ * @clk: struct clk * to program
|
|
|
+ * @v: clksel bitfield value to program (with LSB at bit 0)
|
|
|
+ *
|
|
|
+ * Shift the clksel register bitfield value @v to its appropriate
|
|
|
+ * location in the clksel register and write it in. This function
|
|
|
+ * will ensure that the write to the clksel_reg reaches its
|
|
|
+ * destination before returning -- important since PRM and CM register
|
|
|
+ * accesses can be quite slow compared to ARM cycles -- but does not
|