* @dev: struct device
* @oh: module
* @sysc_fields: sysc register bits
+ * @clockdomain: clockdomain
* @rev_offs: revision register offset
* @sysc_offs: sysconfig register offset
* @syss_offs: sysstatus register offset
static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh,
const struct ti_sysc_module_data *data,
struct sysc_regbits *sysc_fields,
+ struct clockdomain *clkdm,
s32 rev_offs, s32 sysc_offs,
s32 syss_offs, u32 sysc_flags,
u32 idlemodes)
struct omap_hwmod_class_sysconfig *sysc;
struct omap_hwmod_class *class = NULL;
struct omap_hwmod_ocp_if *oi = NULL;
- struct clockdomain *clkdm = NULL;
- struct clk *clk = NULL;
void __iomem *regs = NULL;
unsigned long flags;
oi->user = OCP_USER_MPU | OCP_USER_SDMA;
}
- if (!oh->_clk) {
- struct clk_hw_omap *hwclk;
-
- clk = of_clk_get_by_name(dev->of_node, "fck");
- if (!IS_ERR(clk))
- clk_prepare(clk);
- else
- clk = NULL;
-
- /*
- * Populate clockdomain based on dts clock. It is needed for
- * clkdm_deny_idle() and clkdm_allow_idle() until we have have
- * interconnect driver and reset driver capable of blocking
- * clockdomain idle during reset, enable and idle.
- */
- if (clk) {
- hwclk = to_clk_hw_omap(__clk_get_hw(clk));
- if (hwclk && hwclk->clkdm_name)
- clkdm = clkdm_lookup(hwclk->clkdm_name);
- }
-
- /*
- * Note that we assume interconnect driver manages the clocks
- * and do not need to populate oh->_clk for dynamically
- * allocated modules.
- */
- clk_unprepare(clk);
- clk_put(clk);
- }
-
spin_lock_irqsave(&oh->_lock, flags);
if (regs)
oh->_mpu_rt_va = regs;
u32 sysc_flags, idlemodes;
int error;
- if (!dev || !data)
+ if (!dev || !data || !data->name || !cookie)
return -EINVAL;
oh = _lookup(data->name);
return error;
return omap_hwmod_allocate_module(dev, oh, data, sysc_fields,
- rev_offs, sysc_offs, syss_offs,
+ cookie->clkdm, rev_offs,
+ sysc_offs, syss_offs,
sysc_flags, idlemodes);
}
#include <linux/platform_data/wkup_m3.h>
#include <linux/platform_data/asoc-ti-mcbsp.h>
+#include "clockdomain.h"
#include "common.h"
#include "common-board-devices.h"
#include "control.h"
}
#endif
+static struct clockdomain *ti_sysc_find_one_clockdomain(struct clk *clk)
+{
+ struct clockdomain *clkdm = NULL;
+ struct clk_hw_omap *hwclk;
+
+ hwclk = to_clk_hw_omap(__clk_get_hw(clk));
+ if (hwclk && hwclk->clkdm_name)
+ clkdm = clkdm_lookup(hwclk->clkdm_name);
+
+ return clkdm;
+}
+
+/**
+ * ti_sysc_clkdm_init - find clockdomain based on clock
+ * @fck: device functional clock
+ * @ick: device interface clock
+ * @dev: struct device
+ *
+ * Populate clockdomain based on clock. It is needed for
+ * clkdm_deny_idle() and clkdm_allow_idle() for blocking clockdomain
+ * clockdomain idle during reset, enable and idle.
+ *
+ * Note that we assume interconnect driver manages the clocks
+ * and do not need to populate oh->_clk for dynamically
+ * allocated modules.
+ */
+static int ti_sysc_clkdm_init(struct device *dev,
+ struct clk *fck, struct clk *ick,
+ struct ti_sysc_cookie *cookie)
+{
+ if (fck)
+ cookie->clkdm = ti_sysc_find_one_clockdomain(fck);
+ if (cookie->clkdm)
+ return 0;
+ if (ick)
+ cookie->clkdm = ti_sysc_find_one_clockdomain(ick);
+ if (cookie->clkdm)
+ return 0;
+
+ return -ENODEV;
+}
+
+static void ti_sysc_clkdm_deny_idle(struct device *dev,
+ const struct ti_sysc_cookie *cookie)
+{
+ if (cookie->clkdm)
+ clkdm_deny_idle(cookie->clkdm);
+}
+
+static void ti_sysc_clkdm_allow_idle(struct device *dev,
+ const struct ti_sysc_cookie *cookie)
+{
+ if (cookie->clkdm)
+ clkdm_allow_idle(cookie->clkdm);
+}
+
static int ti_sysc_enable_module(struct device *dev,
const struct ti_sysc_cookie *cookie)
{
static struct ti_sysc_platform_data ti_sysc_pdata = {
.auxdata = omap_auxdata_lookup,
+ .init_clockdomain = ti_sysc_clkdm_init,
+ .clkdm_deny_idle = ti_sysc_clkdm_deny_idle,
+ .clkdm_allow_idle = ti_sysc_clkdm_allow_idle,
.init_module = omap_hwmod_init_module,
.enable_module = ti_sysc_enable_module,
.idle_module = ti_sysc_idle_module,
}
}
+static void sysc_clkdm_deny_idle(struct sysc *ddata)
+{
+ struct ti_sysc_platform_data *pdata;
+
+ if (ddata->legacy_mode)
+ return;
+
+ pdata = dev_get_platdata(ddata->dev);
+ if (pdata && pdata->clkdm_deny_idle)
+ pdata->clkdm_deny_idle(ddata->dev, &ddata->cookie);
+}
+
+static void sysc_clkdm_allow_idle(struct sysc *ddata)
+{
+ struct ti_sysc_platform_data *pdata;
+
+ if (ddata->legacy_mode)
+ return;
+
+ pdata = dev_get_platdata(ddata->dev);
+ if (pdata && pdata->clkdm_allow_idle)
+ pdata->clkdm_allow_idle(ddata->dev, &ddata->cookie);
+}
+
/**
* sysc_init_resets - init rstctrl reset line if configured
* @ddata: device driver data
#define SYSC_IDLE_MASK (SYSC_NR_IDLEMODES - 1)
+/* Caller needs to manage sysc_clkdm_deny_idle() and sysc_clkdm_allow_idle() */
static int sysc_enable_module(struct device *dev)
{
struct sysc *ddata;
if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV)
return 0;
- /*
- * TODO: Need to prevent clockdomain autoidle?
- * See clkdm_deny_idle() in arch/mach-omap2/omap_hwmod.c
- */
-
regbits = ddata->cap->regbits;
reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
return 0;
}
+/* Caller needs to manage sysc_clkdm_deny_idle() and sysc_clkdm_allow_idle() */
static int sysc_disable_module(struct device *dev)
{
struct sysc *ddata;
if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV)
return 0;
- /*
- * TODO: Need to prevent clockdomain autoidle?
- * See clkdm_deny_idle() in arch/mach-omap2/omap_hwmod.c
- */
-
regbits = ddata->cap->regbits;
reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
if (!ddata->enabled)
return 0;
+ sysc_clkdm_deny_idle(ddata);
+
if (ddata->legacy_mode) {
error = sysc_runtime_suspend_legacy(dev, ddata);
if (error)
- return error;
+ goto err_allow_idle;
} else {
error = sysc_disable_module(dev);
if (error)
- return error;
+ goto err_allow_idle;
}
sysc_disable_main_clocks(ddata);
ddata->enabled = false;
+err_allow_idle:
+ sysc_clkdm_allow_idle(ddata);
+
return error;
}
if (ddata->enabled)
return 0;
+ sysc_clkdm_deny_idle(ddata);
+
if (sysc_opt_clks_needed(ddata)) {
error = sysc_enable_opt_clocks(ddata);
if (error)
- return error;
+ goto err_allow_idle;
}
error = sysc_enable_main_clocks(ddata);
ddata->enabled = true;
+ sysc_clkdm_allow_idle(ddata);
+
return 0;
err_main_clocks:
err_opt_clocks:
if (sysc_opt_clks_needed(ddata))
sysc_disable_opt_clocks(ddata);
+err_allow_idle:
+ sysc_clkdm_allow_idle(ddata);
return error;
}
}
}
+static int sysc_clockdomain_init(struct sysc *ddata)
+{
+ struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev);
+ struct clk *fck = NULL, *ick = NULL;
+ int error;
+
+ if (!pdata || !pdata->init_clockdomain)
+ return 0;
+
+ switch (ddata->nr_clocks) {
+ case 2:
+ ick = ddata->clocks[SYSC_ICK];
+ /* fallthrough */
+ case 1:
+ fck = ddata->clocks[SYSC_FCK];
+ break;
+ case 0:
+ return 0;
+ }
+
+ error = pdata->init_clockdomain(ddata->dev, fck, ick, &ddata->cookie);
+ if (!error || error == -ENODEV)
+ return 0;
+
+ return error;
+}
+
/*
* Note that pdata->init_module() typically does a reset first. After
* pdata->init_module() is done, PM runtime can be used for the interconnect
struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev);
int error;
- if (!ddata->legacy_mode || !pdata || !pdata->init_module)
+ if (!pdata || !pdata->init_module)
return 0;
error = pdata->init_module(ddata->dev, ddata->mdata, &ddata->cookie);
(SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT))
manage_clocks = false;
+ error = sysc_clockdomain_init(ddata);
+ if (error)
+ return error;
+
if (manage_clocks) {
+ sysc_clkdm_deny_idle(ddata);
+
error = sysc_enable_opt_clocks(ddata);
if (error)
return error;
ddata->revision = sysc_read_revision(ddata);
sysc_init_revision_quirks(ddata);
- error = sysc_legacy_init(ddata);
- if (error)
- goto err_main_clocks;
+ if (ddata->legacy_mode) {
+ error = sysc_legacy_init(ddata);
+ if (error)
+ goto err_main_clocks;
+ }
+
+ if (!ddata->legacy_mode && manage_clocks) {
+ error = sysc_enable_module(ddata->dev);
+ if (error)
+ goto err_main_clocks;
+ }
error = sysc_reset(ddata);
if (error)
dev_err(ddata->dev, "Reset failed with %d\n", error);
+ if (!ddata->legacy_mode && manage_clocks)
+ sysc_disable_module(ddata->dev);
+
err_main_clocks:
if (manage_clocks)
sysc_disable_main_clocks(ddata);
err_opt_clocks:
- if (manage_clocks)
+ if (manage_clocks) {
sysc_disable_opt_clocks(ddata);
+ sysc_clkdm_allow_idle(ddata);
+ }
return error;
}
struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev);
struct ti_sysc_module_data *mdata;
- if (!pdata || !ddata->legacy_mode)
+ if (!pdata)
return 0;
mdata = devm_kzalloc(ddata->dev, sizeof(*mdata), GFP_KERNEL);
if (!mdata)
return -ENOMEM;
- mdata->name = ddata->legacy_mode;
- mdata->module_pa = ddata->module_pa;
- mdata->module_size = ddata->module_size;
- mdata->offsets = ddata->offsets;
- mdata->nr_offsets = SYSC_MAX_REGS;
- mdata->cap = ddata->cap;
- mdata->cfg = &ddata->cfg;
+ if (ddata->legacy_mode) {
+ mdata->name = ddata->legacy_mode;
+ mdata->module_pa = ddata->module_pa;
+ mdata->module_size = ddata->module_size;
+ mdata->offsets = ddata->offsets;
+ mdata->nr_offsets = SYSC_MAX_REGS;
+ mdata->cap = ddata->cap;
+ mdata->cfg = &ddata->cfg;
+ }
ddata->mdata = mdata;