* [PATCH v2 05/16] clk: tegra: Add closed loop support for the DFLL
From: Tuomas Tynkkynen @ 2014-07-21 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
With closed loop support, the clock rate of the DFLL can be adjusted.
The oscillator itself in the DFLL is a free-running oscillator whose
rate is directly determined the supply voltage. However, the DFLL
module contains logic to compare the DFLL output rate to a fixed
reference clock (51 MHz) and make a decision to either lower or raise
the DFLL supply voltage. The DFLL module can then autonomously change
the supply voltage by communicating with an off-chip PMIC via either I2C
or PWM signals. This driver currently supports only I2C.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- query the various properties required for I2C mode from the
regulator framework
drivers/clk/tegra/clk-dfll.c | 656 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 653 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
index d83e859..0d4b2dd 100644
--- a/drivers/clk/tegra/clk-dfll.c
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -205,12 +205,16 @@
*/
#define REF_CLOCK_RATE 51000000UL
+#define DVCO_RATE_TO_MULT(rate, ref_rate) ((rate) / ((ref_rate) / 2))
+#define MULT_TO_DVCO_RATE(mult, ref_rate) ((mult) * ((ref_rate) / 2))
/**
* enum dfll_ctrl_mode - DFLL hardware operating mode
* @DFLL_UNINITIALIZED: (uninitialized state - not in hardware bitfield)
* @DFLL_DISABLED: DFLL not generating an output clock
* @DFLL_OPEN_LOOP: DVCO running, but DFLL not adjusting voltage
+ * @DFLL_CLOSED_LOOP: DVCO running, and DFLL adjusting voltage to match
+ * the requested rate
*
* The integer corresponding to the last two states, minus one, is
* written to the DFLL hardware to change operating modes.
@@ -219,6 +223,7 @@ enum dfll_ctrl_mode {
DFLL_UNINITIALIZED = 0,
DFLL_DISABLED = 1,
DFLL_OPEN_LOOP = 2,
+ DFLL_CLOSED_LOOP = 3,
};
/**
@@ -236,6 +241,22 @@ enum dfll_tune_range {
DFLL_TUNE_LOW = 1,
};
+/**
+ * struct dfll_rate_req - target DFLL rate request data
+ * @rate: target frequency, after the postscaling
+ * @dvco_target_rate: target frequency, after the postscaling
+ * @lut_index: LUT index at which voltage the dvco_target_rate will be reached
+ * @mult_bits: value to program to the MULT bits of the DFLL_FREQ_REQ register
+ * @scale_bits: value to program to the SCALE bits of the DFLL_FREQ_REQ register
+ */
+struct dfll_rate_req {
+ unsigned long rate;
+ unsigned long dvco_target_rate;
+ int lut_index;
+ u8 mult_bits;
+ u8 scale_bits;
+};
+
struct tegra_dfll {
struct device *dev;
struct tegra_dfll_soc_data *soc;
@@ -259,9 +280,26 @@ struct tegra_dfll {
struct dentry *debugfs_dir;
struct clk_hw dfll_clk_hw;
const char *output_clock_name;
+ struct dfll_rate_req last_req;
/* Parameters from DT */
u32 droop_ctrl;
+ u32 sample_rate;
+ u32 force_mode;
+ u32 cf;
+ u32 ci;
+ u32 cg;
+ bool cg_scale;
+
+ /* I2C interface parameters */
+ u32 i2c_fs_rate;
+ u32 i2c_reg;
+ u32 i2c_slave_addr;
+
+ /* i2c_lut array entries are regulator framework selectors */
+ unsigned i2c_lut[MAX_DFLL_VOLTAGES];
+ int i2c_lut_size;
+ u8 lut_min, lut_max, lut_safe;
};
#define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
@@ -271,6 +309,7 @@ static const char * const mode_name[] = {
[DFLL_UNINITIALIZED] = "uninitialized",
[DFLL_DISABLED] = "disabled",
[DFLL_OPEN_LOOP] = "open_loop",
+ [DFLL_CLOSED_LOOP] = "closed_loop",
};
/*
@@ -494,6 +533,282 @@ static void dfll_set_mode(struct tegra_dfll *td,
}
/*
+ * DFLL-to-I2C controller interface
+ */
+
+/**
+ * dfll_i2c_set_output_enabled - enable/disable I2C PMIC voltage requests
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the I2C voltage requests
+ *
+ * Set the master enable control for I2C control value updates. If disabled,
+ * then I2C control messages are inhibited, regardless of the DFLL mode.
+ */
+static int dfll_i2c_set_output_enabled(struct tegra_dfll *td, bool enable)
+{
+ u32 val;
+
+ val = dfll_i2c_readl(td, DFLL_OUTPUT_CFG);
+
+ if (enable)
+ val |= DFLL_OUTPUT_CFG_I2C_ENABLE;
+ else
+ val &= ~DFLL_OUTPUT_CFG_I2C_ENABLE;
+
+ dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
+ dfll_i2c_wmb(td);
+
+ return 0;
+}
+
+/**
+ * dfll_load_lut - load the voltage lookup table
+ * @td: struct tegra_dfll *
+ *
+ * Load the voltage-to-PMIC register value lookup table into the DFLL
+ * IP block memory. Look-up tables can be loaded at any time.
+ */
+static void dfll_load_i2c_lut(struct tegra_dfll *td)
+{
+ int i, lut_index;
+ u32 val;
+
+ for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
+ if (i < td->lut_min)
+ lut_index = td->lut_min;
+ else if (i > td->lut_max)
+ lut_index = td->lut_max;
+ else
+ lut_index = i;
+
+ val = regulator_list_hardware_vsel(td->vdd_reg,
+ td->i2c_lut[lut_index]);
+ __raw_writel(val, td->lut_base + i * 4);
+ }
+
+ dfll_i2c_wmb(td);
+}
+
+/**
+ * dfll_init_i2c_if - set up the DFLL's DFLL-I2C interface
+ * @td: DFLL instance
+ *
+ * During DFLL driver initialization, program the DFLL-I2C interface
+ * with the PMU slave address, vdd register offset, and transfer mode.
+ * This data is used by the DFLL to automatically construct I2C
+ * voltage-set commands, which are then passed to the DFLL's internal
+ * I2C controller.
+ */
+static void dfll_init_i2c_if(struct tegra_dfll *td)
+{
+ u32 val;
+
+ if (td->i2c_slave_addr > 0x7f) {
+ val = td->i2c_slave_addr << DFLL_I2C_CFG_SLAVE_ADDR_SHIFT_10BIT;
+ val |= DFLL_I2C_CFG_SLAVE_ADDR_10;
+ } else {
+ val = td->i2c_slave_addr << DFLL_I2C_CFG_SLAVE_ADDR_SHIFT_7BIT;
+ }
+ val |= DFLL_I2C_CFG_SIZE_MASK;
+ val |= DFLL_I2C_CFG_ARB_ENABLE;
+ dfll_i2c_writel(td, val, DFLL_I2C_CFG);
+
+ dfll_i2c_writel(td, td->i2c_reg, DFLL_I2C_VDD_REG_ADDR);
+
+ val = DIV_ROUND_UP(td->i2c_clk_rate, td->i2c_fs_rate * 8);
+ BUG_ON(!val || (val > DFLL_I2C_CLK_DIVISOR_MASK));
+ val = (val - 1) << DFLL_I2C_CLK_DIVISOR_FS_SHIFT;
+
+ /* default hs divisor just in case */
+ val |= 1 << DFLL_I2C_CLK_DIVISOR_HS_SHIFT;
+ __raw_writel(val, td->i2c_controller_base + DFLL_I2C_CLK_DIVISOR);
+ dfll_i2c_wmb(td);
+}
+
+/**
+ * dfll_init_out_if - prepare DFLL-to-PMIC interface
+ * @td: DFLL instance
+ *
+ * During DFLL driver initialization or resume from context loss,
+ * disable the I2C command output to the PMIC, set safe voltage and
+ * output limits, and disable and clear limit interrupts.
+ */
+static void dfll_init_out_if(struct tegra_dfll *td)
+{
+ u32 val;
+
+ td->lut_min = 0;
+ td->lut_max = td->i2c_lut_size - 1;
+ td->lut_safe = td->lut_min + 1;
+
+ dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
+ val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
+ (td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
+ (td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
+ dfll_writel(td, val, DFLL_OUTPUT_CFG);
+ dfll_wmb(td);
+
+ dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
+ dfll_i2c_writel(td, 0, DFLL_INTR_EN);
+ dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
+ DFLL_INTR_STS);
+
+ dfll_load_i2c_lut(td);
+ dfll_init_i2c_if(td);
+}
+
+/*
+ * Set/get the DFLL's targeted output clock rate
+ */
+
+/**
+ * find_lut_index_for_rate - determine I2C LUT index for given DFLL rate
+ * @td: DFLL instance
+ * @rate: clock rate
+ *
+ * Determines the index of a I2C LUT entry for a voltage that approximately
+ * produces the given DFLL clock rate. This is used when forcing a value
+ * to the integrator during rate changes. Returns -ENOENT if a suitable
+ * LUT index is not found.
+ */
+static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate)
+{
+ struct dev_pm_opp *opp;
+ int i, uv;
+
+ opp = dev_pm_opp_find_freq_ceil(td->soc->opp_dev, &rate);
+ if (IS_ERR(opp))
+ return PTR_ERR(opp);
+ uv = dev_pm_opp_get_voltage(opp);
+
+ for (i = 0; i < td->i2c_lut_size; i++) {
+ if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv)
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * dfll_calculate_rate_request - calculate DFLL parameters for a given rate
+ * @td: DFLL instance
+ * @req: DFLL-rate-request structure
+ * @rate: the desired DFLL rate
+ *
+ * Populate the DFLL-rate-request record @req fields with the scale_bits
+ * and mult_bits fields, based on the target input rate. Returns 0 upon
+ * success, or -EINVAL if the requested rate in req->rate is too high
+ * or low for the DFLL to generate.
+ */
+static int dfll_calculate_rate_request(struct tegra_dfll *td,
+ struct dfll_rate_req *req,
+ unsigned long rate)
+{
+ u32 val;
+
+ /*
+ * If requested rate is below the minimum DVCO rate, active the scaler.
+ * In the future the DVCO minimum voltage should be selected based on
+ * chip temperature and the actual minimum rate should be calibrated
+ * at runtime.
+ */
+ req->scale_bits = DFLL_FREQ_REQ_SCALE_MAX - 1;
+ if (rate < td->dvco_rate_min) {
+ int scale;
+
+ scale = DIV_ROUND_CLOSEST(rate / 1000 * DFLL_FREQ_REQ_SCALE_MAX,
+ td->dvco_rate_min / 1000);
+ if (!scale) {
+ dev_err(td->dev, "%s: Rate %lu is too low\n",
+ __func__, rate);
+ return -EINVAL;
+ }
+ req->scale_bits = scale - 1;
+ rate = td->dvco_rate_min;
+ }
+
+ /* Convert requested rate into frequency request and scale settings */
+ val = DVCO_RATE_TO_MULT(rate, td->ref_rate);
+ if (val > FREQ_MAX) {
+ dev_err(td->dev, "%s: Rate %lu is above dfll range\n",
+ __func__, rate);
+ return -EINVAL;
+ }
+ req->mult_bits = val;
+ req->dvco_target_rate = MULT_TO_DVCO_RATE(req->mult_bits, td->ref_rate);
+ req->rate = dfll_scale_dvco_rate(req->dvco_target_rate,
+ req->scale_bits);
+ req->lut_index = find_lut_index_for_rate(td, req->dvco_target_rate);
+ if (req->lut_index < 0)
+ return req->lut_index;
+
+ return 0;
+}
+
+/**
+ * dfll_set_frequency_request - start the frequency change operation
+ * @td: DFLL instance
+ * @req: rate request structure
+ *
+ * Tell the DFLL to try to change its output frequency to the
+ * frequency represented by @req. DFLL must be in closed-loop mode.
+ */
+static void dfll_set_frequency_request(struct tegra_dfll *td,
+ struct dfll_rate_req *req)
+{
+ u32 val = 0;
+ int force_val;
+ int coef = 128; /* FIXME: td->cg_scale? */;
+
+ force_val = (req->lut_index - td->lut_safe) * coef / td->cg;
+ force_val = clamp(force_val, FORCE_MIN, FORCE_MAX);
+
+ val |= req->mult_bits << DFLL_FREQ_REQ_MULT_SHIFT;
+ val |= req->scale_bits << DFLL_FREQ_REQ_SCALE_SHIFT;
+ val |= ((u32)force_val << DFLL_FREQ_REQ_FORCE_SHIFT) &
+ DFLL_FREQ_REQ_FORCE_MASK;
+ val |= DFLL_FREQ_REQ_FREQ_VALID | DFLL_FREQ_REQ_FORCE_ENABLE;
+
+ dfll_writel(td, val, DFLL_FREQ_REQ);
+ dfll_wmb(td);
+}
+
+/**
+ * tegra_dfll_request_rate - set the next rate for the DFLL to tune to
+ * @td: DFLL instance
+ * @rate: clock rate to target
+ *
+ * Convert the requested clock rate @rate into the DFLL control logic
+ * settings. In closed-loop mode, update new settings immediately to
+ * adjust DFLL output rate accordingly. Otherwise, just save them
+ * until the next switch to closed loop. Returns 0 upon success,
+ * -EPERM if the DFLL driver has not yet been initialized, or -EINVAL
+ * if @rate is outside the DFLL's tunable range.
+ */
+static int dfll_request_rate(struct tegra_dfll *td, unsigned long rate)
+{
+ int ret;
+ struct dfll_rate_req req;
+
+ if (td->mode == DFLL_UNINITIALIZED) {
+ dev_err(td->dev, "%s: Cannot set DFLL rate in %s mode\n",
+ __func__, mode_name[td->mode]);
+ return -EPERM;
+ }
+
+ ret = dfll_calculate_rate_request(td, &req, rate);
+ if (ret)
+ return ret;
+
+ td->last_req = req;
+
+ if (td->mode == DFLL_CLOSED_LOOP)
+ dfll_set_frequency_request(td, &td->last_req);
+
+ return 0;
+}
+
+/*
* DFLL enable/disable & open-loop <-> closed-loop transitions
*/
@@ -565,8 +880,76 @@ static void dfll_set_open_loop_config(struct tegra_dfll *td)
dfll_wmb(td);
}
+/**
+ * tegra_dfll_lock - switch from open-loop to closed-loop mode
+ * @td: DFLL instance
+ *
+ * Switch from OPEN_LOOP state to CLOSED_LOOP state. Returns 0 upon success,
+ * -EINVAL if the DFLL's target rate hasn't been set yet, or -EPERM if the
+ * DFLL is not currently in open-loop mode.
+ */
+static int dfll_lock(struct tegra_dfll *td)
+{
+ struct dfll_rate_req *req = &td->last_req;
+
+ switch (td->mode) {
+ case DFLL_CLOSED_LOOP:
+ return 0;
+
+ case DFLL_OPEN_LOOP:
+ if (req->rate == 0) {
+ dev_err(td->dev, "%s: Cannot lock DFLL at rate 0\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ dfll_i2c_set_output_enabled(td, true);
+ dfll_set_mode(td, DFLL_CLOSED_LOOP);
+ dfll_set_frequency_request(td, req);
+ return 0;
+
+ default:
+ BUG_ON(td->mode > DFLL_CLOSED_LOOP);
+ dev_err(td->dev, "%s: Cannot lock DFLL in %s mode\n",
+ __func__, mode_name[td->mode]);
+ return -EPERM;
+ }
+}
+
+/**
+ * tegra_dfll_unlock - switch from closed-loop to open-loop mode
+ * @td: DFLL instance
+ *
+ * Switch from CLOSED_LOOP state to OPEN_LOOP state. Returns 0 upon success,
+ * or -EPERM if the DFLL is not currently in open-loop mode.
+ */
+static int dfll_unlock(struct tegra_dfll *td)
+{
+ switch (td->mode) {
+ case DFLL_CLOSED_LOOP:
+ dfll_set_open_loop_config(td);
+ dfll_set_mode(td, DFLL_OPEN_LOOP);
+ dfll_i2c_set_output_enabled(td, false);
+ return 0;
+
+ case DFLL_OPEN_LOOP:
+ return 0;
+
+ default:
+ BUG_ON(td->mode > DFLL_CLOSED_LOOP);
+ dev_err(td->dev, "%s: Cannot unlock DFLL in %s mode\n",
+ __func__, mode_name[td->mode]);
+ return -EPERM;
+ }
+}
+
/*
* Clock framework integration
+ *
+ * When the DFLL is being controlled by the CCF, always enter closed loop
+ * mode when the clk is enabled. This requires that a DFLL rate request
+ * has been set beforehand, which implies that a clk_set_rate() call is
+ * always required before a clk_enable().
*/
static int dfll_clk_is_enabled(struct clk_hw *hw)
@@ -579,21 +962,67 @@ static int dfll_clk_is_enabled(struct clk_hw *hw)
static int dfll_clk_enable(struct clk_hw *hw)
{
struct tegra_dfll *td = clk_hw_to_dfll(hw);
+ int ret;
+
+ ret = dfll_enable(td);
+ if (ret)
+ return ret;
+
+ ret = dfll_lock(td);
+ if (ret)
+ dfll_disable(td);
- return dfll_enable(td);
+ return ret;
}
static void dfll_clk_disable(struct clk_hw *hw)
{
struct tegra_dfll *td = clk_hw_to_dfll(hw);
+ int ret;
+
+ ret = dfll_unlock(td);
+ if (!ret)
+ dfll_disable(td);
+}
+
+static unsigned long dfll_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_dfll *td = clk_hw_to_dfll(hw);
- dfll_disable(td);
+ return td->last_req.rate;
+}
+
+static long dfll_clk_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct tegra_dfll *td = clk_hw_to_dfll(hw);
+ struct dfll_rate_req req;
+ int ret;
+
+ ret = dfll_calculate_rate_request(td, &req, rate);
+ if (ret)
+ return ret;
+
+ return req.rate;
+}
+
+static int dfll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_dfll *td = clk_hw_to_dfll(hw);
+
+ return dfll_request_rate(td, rate);
}
static const struct clk_ops dfll_clk_ops = {
.is_enabled = dfll_clk_is_enabled,
.enable = dfll_clk_enable,
.disable = dfll_clk_disable,
+ .recalc_rate = dfll_clk_recalc_rate,
+ .round_rate = dfll_clk_round_rate,
+ .set_rate = dfll_clk_set_rate,
};
static struct clk_init_data dfll_clk_init_data = {
@@ -675,6 +1104,23 @@ static int attr_enable_set(void *data, u64 val)
DEFINE_SIMPLE_ATTRIBUTE(enable_fops, attr_enable_get, attr_enable_set,
"%llu\n");
+static int attr_lock_get(void *data, u64 *val)
+{
+ struct tegra_dfll *td = data;
+
+ *val = (td->mode == DFLL_CLOSED_LOOP);
+
+ return 0;
+}
+static int attr_lock_set(void *data, u64 val)
+{
+ struct tegra_dfll *td = data;
+
+ return val ? dfll_lock(td) : dfll_unlock(td);
+}
+DEFINE_SIMPLE_ATTRIBUTE(lock_fops, attr_lock_get, attr_lock_set,
+ "%llu\n");
+
static int attr_rate_get(void *data, u64 *val)
{
struct tegra_dfll *td = data;
@@ -683,7 +1129,14 @@ static int attr_rate_get(void *data, u64 *val)
return 0;
}
-DEFINE_SIMPLE_ATTRIBUTE(rate_fops, attr_rate_get, NULL, "%llu\n");
+
+static int attr_rate_set(void *data, u64 val)
+{
+ struct tegra_dfll *td = data;
+
+ return dfll_request_rate(td, val);
+}
+DEFINE_SIMPLE_ATTRIBUTE(rate_fops, attr_rate_get, attr_rate_set, "%llu\n");
static int attr_registers_show(struct seq_file *s, void *data)
{
@@ -745,6 +1198,10 @@ static int dfll_debug_init(struct tegra_dfll *td)
td->debugfs_dir, td, &enable_fops))
goto err_out;
+ if (!debugfs_create_file("lock", S_IRUGO,
+ td->debugfs_dir, td, &lock_fops))
+ goto err_out;
+
if (!debugfs_create_file("rate", S_IRUGO,
td->debugfs_dir, td, &rate_fops))
goto err_out;
@@ -776,6 +1233,19 @@ err_out:
*/
static void dfll_set_default_params(struct tegra_dfll *td)
{
+ u32 val;
+
+ val = DIV_ROUND_UP(td->ref_rate, td->sample_rate * 32);
+ BUG_ON(val > DFLL_CONFIG_DIV_MASK);
+ dfll_writel(td, val, DFLL_CONFIG);
+
+ val = (td->force_mode << DFLL_PARAMS_FORCE_MODE_SHIFT) |
+ (td->cf << DFLL_PARAMS_CF_PARAM_SHIFT) |
+ (td->ci << DFLL_PARAMS_CI_PARAM_SHIFT) |
+ (td->cg << DFLL_PARAMS_CG_PARAM_SHIFT) |
+ (td->cg_scale ? DFLL_PARAMS_CG_SCALE : 0);
+ dfll_writel(td, val, DFLL_PARAMS);
+
dfll_tune_low(td);
dfll_writel(td, td->droop_ctrl, DFLL_DROOP_CTRL);
dfll_writel(td, DFLL_MONITOR_CTRL_FREQ, DFLL_MONITOR_CTRL);
@@ -865,6 +1335,8 @@ static int dfll_init(struct tegra_dfll *td)
dfll_set_open_loop_config(td);
+ dfll_init_out_if(td);
+
pm_runtime_put_sync(td->dev);
return 0;
@@ -884,6 +1356,130 @@ di_err1:
* DT data fetch
*/
+/*
+ * Find a PMIC voltage register-to-voltage mapping for the given voltage.
+ * An exact voltage match is required.
+ */
+static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
+{
+ int i, n_voltages, reg_uV;
+
+ n_voltages = regulator_count_voltages(td->vdd_reg);
+ for (i = 0; i < n_voltages; i++) {
+ reg_uV = regulator_list_voltage(td->vdd_reg, i);
+ if (reg_uV < 0)
+ break;
+
+ if (uV == reg_uV)
+ return i;
+ }
+
+ dev_err(td->dev, "no voltage map entry for %d uV\n", uV);
+ return -EINVAL;
+}
+
+/*
+ * Find a PMIC voltage register-to-voltage mapping for the given voltage,
+ * rounding up to the closest supported voltage.
+ * */
+static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
+{
+ int i, n_voltages, reg_uV;
+
+ n_voltages = regulator_count_voltages(td->vdd_reg);
+ for (i = 0; i < n_voltages; i++) {
+ reg_uV = regulator_list_voltage(td->vdd_reg, i);
+ if (reg_uV < 0)
+ break;
+
+ if (uV <= reg_uV)
+ return i;
+ }
+
+ dev_err(td->dev, "no voltage map entry rounding to %d uV\n", uV);
+ return -EINVAL;
+}
+
+/**
+ * dfll_build_i2c_lut - build the I2C voltage register lookup table
+ * @td: DFLL instance
+ *
+ * The DFLL hardware has 33 bytes of look-up table RAM that must be filled with
+ * PMIC voltage register values that span the entire DFLL operating range.
+ * This function builds the look-up table based on the OPP table provided by
+ * the soc-specific platform driver (td->soc->opp_dev) and the PMIC
+ * register-to-voltage mapping queried from the regulator framework.
+ *
+ * On success, fills in td->i2c_lut and returns 0, or -err on failure.
+ */
+static int dfll_build_i2c_lut(struct tegra_dfll *td)
+{
+ int ret = -EINVAL;
+ int j, v, v_max, v_opp;
+ int selector;
+ unsigned long rate;
+ struct dev_pm_opp *opp;
+
+ rcu_read_lock();
+
+ rate = ULONG_MAX;
+ opp = dev_pm_opp_find_freq_floor(td->soc->opp_dev, &rate);
+ if (IS_ERR(opp)) {
+ dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
+ goto out;
+ }
+ v_max = dev_pm_opp_get_voltage(opp);
+
+ v = td->soc->min_millivolts * 1000;
+ td->i2c_lut[0] = find_vdd_map_entry_exact(td, v);
+ if (td->i2c_lut[0] < 0)
+ goto out;
+
+ for (j = 1, rate = 0; ; rate++) {
+ opp = dev_pm_opp_find_freq_ceil(td->soc->opp_dev, &rate);
+ if (IS_ERR(opp))
+ break;
+ v_opp = dev_pm_opp_get_voltage(opp);
+
+ if (v_opp <= td->soc->min_millivolts * 1000)
+ td->dvco_rate_min = dev_pm_opp_get_freq(opp);
+
+ for (;;) {
+ v += max(1, (v_max - v) / (MAX_DFLL_VOLTAGES - j));
+ if (v >= v_opp)
+ break;
+
+ selector = find_vdd_map_entry_min(td, v);
+ if (selector < 0)
+ goto out;
+ if (selector != td->i2c_lut[j - 1])
+ td->i2c_lut[j++] = selector;
+ }
+
+ v = (j == MAX_DFLL_VOLTAGES - 1) ? v_max : v_opp;
+ selector = find_vdd_map_entry_exact(td, v);
+ if (selector < 0)
+ goto out;
+ if (selector != td->i2c_lut[j - 1])
+ td->i2c_lut[j++] = selector;
+
+ if (v >= v_max)
+ break;
+ }
+ td->i2c_lut_size = j;
+
+ if (!td->dvco_rate_min)
+ dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n",
+ td->soc->min_millivolts);
+ else
+ ret = 0;
+
+out:
+ rcu_read_unlock();
+
+ return ret;
+}
+
/**
* read_dt_param - helper function for reading required parameters from the DT
* @td: DFLL instance
@@ -908,6 +1504,49 @@ static bool read_dt_param(struct tegra_dfll *td, const char *param, u32 *dest)
}
/**
+ * dfll_fetch_i2c_params - query PMIC I2C params from DT & regulator subsystem
+ * @td: DFLL instance
+ *
+ * Read all the parameters required for operation in I2C mode. The parameters
+ * can originate from the device tree or the regulator subsystem.
+ * Returns 0 on success or -err on failure.
+ */
+static int dfll_fetch_i2c_params(struct tegra_dfll *td)
+{
+ struct regmap *regmap;
+ struct device *i2c_dev;
+ struct i2c_client *i2c_client;
+ int vsel_reg, vsel_mask;
+ int ret;
+
+ if (!read_dt_param(td, "nvidia,i2c-fs-rate", &td->i2c_fs_rate))
+ return -EINVAL;
+
+ regmap = regulator_get_regmap(td->vdd_reg);
+ i2c_dev = regmap_get_device(regmap);
+ i2c_client = to_i2c_client(i2c_dev);
+
+ td->i2c_slave_addr = i2c_client->addr;
+
+ ret = regulator_get_hardware_vsel_register(td->vdd_reg,
+ &vsel_reg,
+ &vsel_mask);
+ if (ret < 0) {
+ dev_err(td->dev,
+ "regulator unsuitable for DFLL I2C operation\n");
+ return -EINVAL;
+ }
+
+ ret = dfll_build_i2c_lut(td);
+ if (ret) {
+ dev_err(td->dev, "couldn't build I2C LUT\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
* dfll_fetch_common_params - read DFLL parameters from the device tree
* @td: DFLL instance
*
@@ -919,6 +1558,13 @@ static int dfll_fetch_common_params(struct tegra_dfll *td)
bool ok = true;
ok &= read_dt_param(td, "nvidia,droop-ctrl", &td->droop_ctrl);
+ ok &= read_dt_param(td, "nvidia,sample-rate", &td->sample_rate);
+ ok &= read_dt_param(td, "nvidia,force-mode", &td->force_mode);
+ ok &= read_dt_param(td, "nvidia,cf", &td->cf);
+ ok &= read_dt_param(td, "nvidia,ci", &td->ci);
+ ok &= read_dt_param(td, "nvidia,cg", &td->cg);
+ td->cg_scale = of_property_read_bool(td->dev->of_node,
+ "nvidia,cg-scale");
if (of_property_read_string(td->dev->of_node, "clock-output-names",
&td->output_clock_name)) {
@@ -973,6 +1619,10 @@ int tegra_dfll_register(struct platform_device *pdev,
return ret;
}
+ ret = dfll_fetch_i2c_params(td);
+ if (ret)
+ return ret;
+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(td->dev, "no control register resource\n");
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 06/16] clk: tegra: Add functions for parsing CVB tables
From: Tuomas Tynkkynen @ 2014-07-21 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
Tegra CVB tables encode the relationship between operating voltage and
optimal frequency as a second-order polynomial of the so-called speedo
value. The speedo value is written to the on-chip fuses at the factory,
which allows the voltage-frequency operating points to be calculated on
an per-chip basis.
Add utility functions to parse the Tegra-specific tables and export the
voltage-frequency pairs to the generic OPP framework for other drivers
to use.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- none
arch/arm/mach-tegra/Kconfig | 1 +
drivers/clk/tegra/cvb.c | 133 ++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/tegra/cvb.h | 67 ++++++++++++++++++++++
3 files changed, 201 insertions(+)
create mode 100644 drivers/clk/tegra/cvb.c
create mode 100644 drivers/clk/tegra/cvb.h
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 0953996..0d5832f 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -7,6 +7,7 @@ menuconfig ARCH_TEGRA
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select PINCTRL
+ select PM_OPP
select ARCH_HAS_RESET_CONTROLLER
select RESET_CONTROLLER
select SOC_BUS
diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c
new file mode 100644
index 0000000..69c74ee
--- /dev/null
+++ b/drivers/clk/tegra/cvb.c
@@ -0,0 +1,133 @@
+/*
+ * Utility functions for parsing Tegra CVB voltage tables
+ *
+ * Copyright (C) 2012-2014 NVIDIA Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/pm_opp.h>
+
+#include "cvb.h"
+
+/* cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) */
+static inline int get_cvb_voltage(int speedo, int s_scale,
+ const struct cvb_coefficients *cvb)
+{
+ int mv;
+
+ /* apply only speedo scale: output mv = cvb_mv * v_scale */
+ mv = DIV_ROUND_CLOSEST(cvb->c2 * speedo, s_scale);
+ mv = DIV_ROUND_CLOSEST((mv + cvb->c1) * speedo, s_scale) + cvb->c0;
+ return mv;
+}
+
+static int round_cvb_voltage(int mv, int v_scale,
+ const struct rail_alignment *align)
+{
+ /* combined: apply voltage scale and round to cvb alignment step */
+ int uv;
+ int step = (align->step_uv ? : 1000) * v_scale;
+ int offset = align->offset_uv * v_scale;
+
+ uv = max(mv * 1000, offset) - offset;
+ uv = DIV_ROUND_UP(uv, step) * align->step_uv + align->offset_uv;
+ return uv / 1000;
+}
+
+enum {
+ DOWN,
+ UP
+};
+
+static int round_voltage(int mv, const struct rail_alignment *align, int up)
+{
+ if (align->step_uv) {
+ int uv;
+
+ uv = max(mv * 1000, align->offset_uv) - align->offset_uv;
+ uv = (uv + (up ? align->step_uv - 1 : 0)) / align->step_uv;
+ return (uv * align->step_uv + align->offset_uv) / 1000;
+ }
+ return mv;
+}
+
+static int build_opp_table(const struct cvb_table *d,
+ int speedo_value,
+ unsigned long max_freq,
+ struct device *opp_dev)
+{
+ int i, ret, dfll_mv, min_mv, max_mv;
+ const struct cvb_table_freq_entry *table = NULL;
+ const struct rail_alignment *align = &d->alignment;
+
+ min_mv = round_voltage(d->min_millivolts, align, UP);
+ max_mv = round_voltage(d->max_millivolts, align, DOWN);
+
+ for (i = 0; i < MAX_DVFS_FREQS; i++) {
+ table = &d->cvb_table[i];
+ if (!table->freq || (table->freq > max_freq))
+ break;
+
+ dfll_mv = get_cvb_voltage(
+ speedo_value, d->speedo_scale, &table->coefficients);
+ dfll_mv = round_cvb_voltage(dfll_mv, d->voltage_scale, align);
+ dfll_mv = clamp(dfll_mv, min_mv, max_mv);
+
+ ret = dev_pm_opp_add(opp_dev, table->freq, dfll_mv * 1000);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * tegra_cvb_build_opp_table - build OPP table from Tegra CVB tables
+ * @cvb_tables: array of CVB tables
+ * @sz: size of the previously mentioned array
+ * @process_id: process id of the HW module
+ * @speedo_id: speedo id of the HW module
+ * @speedo_value: speedo value of the HW module
+ * @max_rate: highest safe clock rate
+ * @opp_dev: the struct device * for which the OPP table is built
+ *
+ * On Tegra, a CVB table encodes the relationship between operating voltage
+ * and safe maximal frequency for a given module (e.g. GPU or CPU). This
+ * function calculates the optimal voltage-frequency operating points
+ * for the given arguments and exports them via the OPP library for the
+ * given @opp_dev. Returns a pointer to the struct cvb_table that matched
+ * or an ERR_PTR on failure.
+ */
+const struct cvb_table *tegra_cvb_build_opp_table(
+ const struct cvb_table *cvb_tables,
+ size_t sz, int process_id,
+ int speedo_id, int speedo_value,
+ unsigned long max_rate,
+ struct device *opp_dev)
+{
+ int i, ret;
+
+ for (i = 0; i < sz; i++) {
+ const struct cvb_table *d = &cvb_tables[i];
+
+ if (d->speedo_id != -1 && d->speedo_id != speedo_id)
+ continue;
+ if (d->process_id != -1 && d->process_id != process_id)
+ continue;
+
+ ret = build_opp_table(d, speedo_value, max_rate, opp_dev);
+ return ret ? ERR_PTR(ret) : d;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
new file mode 100644
index 0000000..f62cdc4
--- /dev/null
+++ b/drivers/clk/tegra/cvb.h
@@ -0,0 +1,67 @@
+/*
+ * Utility functions for parsing Tegra CVB voltage tables
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#ifndef __DRIVERS_CLK_TEGRA_CVB_H
+#define __DRIVERS_CLK_TEGRA_CVB_H
+
+#include <linux/types.h>
+
+struct device;
+
+#define MAX_DVFS_FREQS 40
+
+struct rail_alignment {
+ int offset_uv;
+ int step_uv;
+};
+
+struct cvb_coefficients {
+ int c0;
+ int c1;
+ int c2;
+};
+
+struct cvb_table_freq_entry {
+ unsigned long freq;
+ struct cvb_coefficients coefficients;
+};
+
+struct cvb_cpu_dfll_data {
+ u32 tune0_low;
+ u32 tune0_high;
+ u32 tune1;
+};
+
+struct cvb_table {
+ int speedo_id;
+ int process_id;
+
+ int min_millivolts;
+ int max_millivolts;
+ struct rail_alignment alignment;
+
+ int speedo_scale;
+ int voltage_scale;
+ struct cvb_table_freq_entry cvb_table[MAX_DVFS_FREQS];
+ struct cvb_cpu_dfll_data cpu_dfll_data;
+};
+
+const struct cvb_table *tegra_cvb_build_opp_table(
+ const struct cvb_table *cvb_tables,
+ size_t sz, int process_id,
+ int speedo_id, int speedo_value,
+ unsigned long max_rate,
+ struct device *opp_dev);
+
+#endif
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 07/16] clk: tegra: Add DFLL DVCO reset control for Tegra124
From: Tuomas Tynkkynen @ 2014-07-21 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
From: Paul Walmsley <pwalmsley@nvidia.com>
The DVCO present in the DFLL IP block has a separate reset line,
exposed via the CAR IP block. This reset line is asserted upon SoC
reset. Unless something (such as the DFLL driver) deasserts this
line, the DVCO will not oscillate, although reads and writes to the
DFLL IP block will complete.
Thanks to Aleksandr Frid <afrid@nvidia.com> for identifying this and
saving hours of debugging time.
Signed-off-by: Paul Walmsley <pwalmsley@nvidia.com>
[ttynkkynen: ported to tegra124 from tegra114]
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- none
drivers/clk/tegra/clk-tegra124.c | 47 ++++++++++++++++++++++++++++++++++++++++
drivers/clk/tegra/clk.h | 3 +++
2 files changed, 50 insertions(+)
diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c
index 80efe51..77fbf38 100644
--- a/drivers/clk/tegra/clk-tegra124.c
+++ b/drivers/clk/tegra/clk-tegra124.c
@@ -31,6 +31,9 @@
#define CLK_SOURCE_CSITE 0x1d4
#define CLK_SOURCE_EMC 0x19c
+#define RST_DFLL_DVCO 0x2f4
+#define DVFS_DFLL_RESET_SHIFT 0
+
#define PLLC_BASE 0x80
#define PLLC_OUT 0x84
#define PLLC_MISC2 0x88
@@ -1378,6 +1381,50 @@ static void __init tegra124_clock_apply_init_table(void)
tegra_init_from_table(init_table, clks, TEGRA124_CLK_CLK_MAX);
}
+/**
+ * tegra124_car_barrier - wait for pending writes to the CAR to complete
+ *
+ * Wait for any outstanding writes to the CAR MMIO space from this CPU
+ * to complete before continuing execution. No return value.
+ */
+static void tegra124_car_barrier(void)
+{
+ readl_relaxed(clk_base + RST_DFLL_DVCO);
+}
+
+/**
+ * tegra124_clock_assert_dfll_dvco_reset - assert the DFLL's DVCO reset
+ *
+ * Assert the reset line of the DFLL's DVCO. No return value.
+ */
+void tegra124_clock_assert_dfll_dvco_reset(void)
+{
+ u32 v;
+
+ v = readl_relaxed(clk_base + RST_DFLL_DVCO);
+ v |= (1 << DVFS_DFLL_RESET_SHIFT);
+ writel_relaxed(v, clk_base + RST_DFLL_DVCO);
+ tegra124_car_barrier();
+}
+EXPORT_SYMBOL(tegra124_clock_assert_dfll_dvco_reset);
+
+/**
+ * tegra124_clock_deassert_dfll_dvco_reset - deassert the DFLL's DVCO reset
+ *
+ * Deassert the reset line of the DFLL's DVCO, allowing the DVCO to
+ * operate. No return value.
+ */
+void tegra124_clock_deassert_dfll_dvco_reset(void)
+{
+ u32 v;
+
+ v = readl_relaxed(clk_base + RST_DFLL_DVCO);
+ v &= ~(1 << DVFS_DFLL_RESET_SHIFT);
+ writel_relaxed(v, clk_base + RST_DFLL_DVCO);
+ tegra124_car_barrier();
+}
+EXPORT_SYMBOL(tegra124_clock_deassert_dfll_dvco_reset);
+
static void __init tegra124_clock_init(struct device_node *np)
{
struct device_node *node;
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index 16ec8d6..4b3f3d0 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -627,6 +627,9 @@ void tegra114_clock_tune_cpu_trimmers_init(void);
void tegra114_clock_assert_dfll_dvco_reset(void);
void tegra114_clock_deassert_dfll_dvco_reset(void);
+void tegra124_clock_assert_dfll_dvco_reset(void);
+void tegra124_clock_deassert_dfll_dvco_reset(void);
+
typedef void (*tegra_clk_apply_init_table_func)(void);
extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 08/16] clk: tegra: Add Tegra124 DFLL clocksource platform driver
From: Tuomas Tynkkynen @ 2014-07-21 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
Add basic platform driver support for the fast CPU cluster DFLL
clocksource found on Tegra124 SoCs. This small driver selects the
appropriate Tegra124-specific characterization data and integration
code. It relies on the DFLL common code to do most of the work.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- moved most of the init code to module_init, otherwise the OPP
tables could get filled multiple times on probe deferral, leading
to lots of warnings
- #include <linux/tegra-soc.h> changed to #include <soc/tegra/fuse.h>
drivers/clk/tegra/Makefile | 2 +
drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 165 +++++++++++++++++++++++++++++
2 files changed, 167 insertions(+)
create mode 100644 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
index 47320ca..2f87188 100644
--- a/drivers/clk/tegra/Makefile
+++ b/drivers/clk/tegra/Makefile
@@ -16,3 +16,5 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o
+obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124-dfll-fcpu.o
+obj-y += cvb.o
diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
new file mode 100644
index 0000000..f068360
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
@@ -0,0 +1,165 @@
+/*
+ * Tegra124 DFLL FCPU clock source driver
+ *
+ * Copyright (C) 2012-2014 NVIDIA Corporation. All rights reserved.
+ *
+ * Aleksandr Frid <afrid@nvidia.com>
+ * Paul Walmsley <pwalmsley@nvidia.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cpu.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <soc/tegra/fuse.h>
+
+#include "clk.h"
+#include "clk-dfll.h"
+#include "cvb.h"
+
+/* Maximum CPU frequency, indexed by CPU speedo id */
+static const unsigned long cpu_max_freq_table[] = {
+ [0] = 2014500000UL,
+ [1] = 2320500000UL,
+ [2] = 2116500000UL,
+ [3] = 2524500000UL,
+};
+
+static const struct cvb_table tegra124_cpu_cvb_tables[] = {
+ {
+ .speedo_id = -1,
+ .process_id = -1,
+ .min_millivolts = 900,
+ .max_millivolts = 1260,
+ .alignment = {
+ .step_uv = 10000, /* 10mV */
+ },
+ .speedo_scale = 100,
+ .voltage_scale = 1000,
+ .cvb_table = {
+ {204000000UL, {1112619, -29295, 402} },
+ {306000000UL, {1150460, -30585, 402} },
+ {408000000UL, {1190122, -31865, 402} },
+ {510000000UL, {1231606, -33155, 402} },
+ {612000000UL, {1274912, -34435, 402} },
+ {714000000UL, {1320040, -35725, 402} },
+ {816000000UL, {1366990, -37005, 402} },
+ {918000000UL, {1415762, -38295, 402} },
+ {1020000000UL, {1466355, -39575, 402} },
+ {1122000000UL, {1518771, -40865, 402} },
+ {1224000000UL, {1573009, -42145, 402} },
+ {1326000000UL, {1629068, -43435, 402} },
+ {1428000000UL, {1686950, -44715, 402} },
+ {1530000000UL, {1746653, -46005, 402} },
+ {1632000000UL, {1808179, -47285, 402} },
+ {1734000000UL, {1871526, -48575, 402} },
+ {1836000000UL, {1936696, -49855, 402} },
+ {1938000000UL, {2003687, -51145, 402} },
+ {2014500000UL, {2054787, -52095, 402} },
+ {2116500000UL, {2124957, -53385, 402} },
+ {2218500000UL, {2196950, -54665, 402} },
+ {2320500000UL, {2270765, -55955, 402} },
+ {2422500000UL, {2346401, -57235, 402} },
+ {2524500000UL, {2437299, -58535, 402} },
+ {0, { 0, 0, 0} },
+ },
+ .cpu_dfll_data = {
+ .tune0_low = 0x005020ff,
+ .tune0_high = 0x005040ff,
+ .tune1 = 0x00000060,
+ }
+ },
+};
+
+static struct tegra_dfll_soc_data soc;
+
+static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
+{
+ return tegra_dfll_register(pdev, &soc);
+}
+
+static struct of_device_id tegra124_dfll_fcpu_of_match[] = {
+ { .compatible = "nvidia,tegra124-dfll", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra124_dfll_fcpu_of_match);
+
+static const struct dev_pm_ops tegra124_dfll_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
+ tegra_dfll_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra124_dfll_fcpu_driver = {
+ .probe = tegra124_dfll_fcpu_probe,
+ .remove = tegra_dfll_unregister,
+ .driver = {
+ .name = "tegra124-dfll",
+ .owner = THIS_MODULE,
+ .of_match_table = tegra124_dfll_fcpu_of_match,
+ .pm = &tegra124_dfll_pm_ops,
+ },
+};
+
+static int __init tegra124_dfll_fcpu_init(void)
+{
+ int process_id, speedo_id, speedo_value;
+ const struct cvb_table *cvb;
+
+ process_id = tegra_sku_info.cpu_process_id;
+ speedo_id = tegra_sku_info.cpu_speedo_id;
+ speedo_value = tegra_sku_info.cpu_speedo_value;
+
+ if (speedo_id >= ARRAY_SIZE(cpu_max_freq_table)) {
+ pr_err("unknown max CPU freq for speedo_id=%d\n", speedo_id);
+ return -ENODEV;
+ }
+
+ soc.opp_dev = get_cpu_device(0);
+ if (!soc.opp_dev) {
+ pr_err("no CPU0 device\n");
+ return -ENODEV;
+ }
+
+ cvb = tegra_cvb_build_opp_table(tegra124_cpu_cvb_tables,
+ ARRAY_SIZE(tegra124_cpu_cvb_tables),
+ process_id, speedo_id, speedo_value,
+ cpu_max_freq_table[speedo_id],
+ soc.opp_dev);
+ if (IS_ERR(cvb)) {
+ pr_err("couldn't build OPP table: %ld\n", PTR_ERR(cvb));
+ return PTR_ERR(cvb);
+ }
+
+ soc.assert_dvco_reset = tegra124_clock_assert_dfll_dvco_reset,
+ soc.deassert_dvco_reset = tegra124_clock_deassert_dfll_dvco_reset,
+ soc.min_millivolts = cvb->min_millivolts;
+ soc.tune0_low = cvb->cpu_dfll_data.tune0_low;
+ soc.tune0_high = cvb->cpu_dfll_data.tune0_high;
+ soc.tune1 = cvb->cpu_dfll_data.tune1;
+
+ return platform_driver_register(&tegra124_dfll_fcpu_driver);
+}
+module_init(tegra124_dfll_fcpu_init);
+
+static void __exit tegra124_dfll_fcpu_exit(void)
+{
+ platform_driver_unregister(&tegra124_dfll_fcpu_driver);
+}
+module_exit(tegra124_dfll_fcpu_exit);
+
+MODULE_DESCRIPTION("Tegra124 DFLL clock source driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Aleksandr Frid <afrid@nvidia.com>");
+MODULE_AUTHOR("Paul Walmsley <pwalmsley@nvidia.com>");
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 09/16] clk: tegra: Save/restore CCLKG_BURST_POLICY on suspend
From: Tuomas Tynkkynen @ 2014-07-21 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
Save and restore this register since the LP1 restore assembly routines
fiddle with it. Otherwise the CPU would keep running on PLLX after
resume from suspend even when DFLL was the original clocksource.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- none
drivers/clk/tegra/clk-tegra124.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c
index 77fbf38..758a4cf 100644
--- a/drivers/clk/tegra/clk-tegra124.c
+++ b/drivers/clk/tegra/clk-tegra124.c
@@ -89,6 +89,8 @@
#define PMC_PLLM_WB0_OVERRIDE 0x1dc
#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0
+#define CCLKG_BURST_POLICY 0x368
+
#define UTMIP_PLL_CFG2 0x488
#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6)
#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18)
@@ -121,6 +123,8 @@
#ifdef CONFIG_PM_SLEEP
static struct cpu_clk_suspend_context {
u32 clk_csite_src;
+ u32 cclkg_burst;
+ u32 cclkg_divider;
} tegra124_cpu_clk_sctx;
#endif
@@ -1318,12 +1322,22 @@ static void tegra124_cpu_clock_suspend(void)
tegra124_cpu_clk_sctx.clk_csite_src =
readl(clk_base + CLK_SOURCE_CSITE);
writel(3 << 30, clk_base + CLK_SOURCE_CSITE);
+
+ tegra124_cpu_clk_sctx.cclkg_burst =
+ readl(clk_base + CCLKG_BURST_POLICY);
+ tegra124_cpu_clk_sctx.cclkg_divider =
+ readl(clk_base + CCLKG_BURST_POLICY + 4);
}
static void tegra124_cpu_clock_resume(void)
{
writel(tegra124_cpu_clk_sctx.clk_csite_src,
clk_base + CLK_SOURCE_CSITE);
+
+ writel(tegra124_cpu_clk_sctx.cclkg_burst,
+ clk_base + CCLKG_BURST_POLICY);
+ writel(tegra124_cpu_clk_sctx.cclkg_divider,
+ clk_base + CCLKG_BURST_POLICY + 4);
}
#endif
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 10/16] clk: tegra: Add the DFLL as a possible parent of the cclk_g clock
From: Tuomas Tynkkynen @ 2014-07-21 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
The DFLL clocksource was missing from the list of possible parents for
the fast CPU cluster. Add it to the list.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- none
drivers/clk/tegra/clk-tegra-super-gen4.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c
index feb3201..f1f4410 100644
--- a/drivers/clk/tegra/clk-tegra-super-gen4.c
+++ b/drivers/clk/tegra/clk-tegra-super-gen4.c
@@ -44,7 +44,9 @@ static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
"pll_p", "pll_p_out4", "unused",
- "unused", "pll_x" };
+ "unused", "pll_x", "unused", "unused",
+ "unused", "unused", "unused", "unused",
+ "dfllCPU_out" };
static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
"pll_p", "pll_p_out4", "unused",
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 11/16] ARM: tegra: Add the DFLL to Tegra124 device tree
From: Tuomas Tynkkynen @ 2014-07-21 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
The DFLL clocksource is a separate IP block from the usual
clock-and-reset controller, so it gets its own device tree node.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- none (only board dts file needed changes)
arch/arm/boot/dts/tegra124.dtsi | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index 03916ef..8ff4332 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -544,6 +544,28 @@
status = "disabled";
};
+ dfll: dfll at 0,70110000 {
+ compatible = "nvidia,tegra124-dfll";
+ reg = <0 0x70110000 0 0x100>, /* DFLL control */
+ <0 0x70110000 0 0x100>, /* I2C output control */
+ <0 0x70110100 0 0x100>, /* Integrated I2C controller */
+ <0 0x70110200 0 0x100>; /* Look-up table RAM */
+ interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA124_CLK_DFLL_SOC>,
+ <&tegra_car TEGRA124_CLK_DFLL_REF>,
+ <&tegra_car TEGRA124_CLK_I2C5>;
+ clock-names = "soc", "ref", "i2c";
+ #clock-cells = <0>;
+ clock-output-names = "dfllCPU_out";
+ nvidia,sample-rate = <12500>;
+ nvidia,droop-ctrl = <0x00000f00>;
+ nvidia,force-mode = <1>;
+ nvidia,cf = <10>;
+ nvidia,ci = <0>;
+ nvidia,cg = <2>;
+ status = "disabled";
+ };
+
ahub at 0,70300000 {
compatible = "nvidia,tegra124-ahub";
reg = <0x0 0x70300000 0x0 0x200>,
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 12/16] ARM: tegra: Enable the DFLL on the Jetson TK1
From: Tuomas Tynkkynen @ 2014-07-21 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
Add the board-specific properties of the DFLL for the Jetson TK1 board.
On this board, the DFLL will take control of the sd0 regulator on the
on-board AS3722 PMIC.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- same changes that were done to the binding in patch 3
* dropped most of the I2C properties, as they are now queried from
the regulator framework
* s/vdd_cpu-supply/vdd-cpu-supply/ to better match what other Tegra
bindings do
arch/arm/boot/dts/tegra124-jetson-tk1.dts | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1.dts b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
index 624b0fb..0c30450 100644
--- a/arch/arm/boot/dts/tegra124-jetson-tk1.dts
+++ b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
@@ -1453,7 +1453,7 @@
vin-ldo9-10-supply = <&vdd_5v0_sys>;
vin-ldo11-supply = <&vdd_3v3_run>;
- sd0 {
+ vdd_cpu: sd0 {
regulator-name = "+VDD_CPU_AP";
regulator-min-microvolt = <700000>;
regulator-max-microvolt = <1400000>;
@@ -1662,6 +1662,12 @@
non-removable;
};
+ dfll at 0,70110000 {
+ status = "okay";
+ vdd-cpu-supply = <&vdd_cpu>;
+ nvidia,i2c-fs-rate = <400000>;
+ };
+
ahub at 0,70300000 {
i2s at 0,70301100 {
status = "okay";
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 13/16] cpufreq: tegra124: Add device tree bindings
From: Tuomas Tynkkynen @ 2014-07-21 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
The cpufreq driver for Tegra124 will be a different one than the old
Tegra20 cpufreq driver (tegra-cpufreq), which does not use the device
tree.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- The clock-latency property was added, as cpufreq-cpu0 wants that
.../bindings/cpufreq/tegra124-cpufreq.txt | 42 ++++++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 Documentation/devicetree/bindings/cpufreq/tegra124-cpufreq.txt
diff --git a/Documentation/devicetree/bindings/cpufreq/tegra124-cpufreq.txt b/Documentation/devicetree/bindings/cpufreq/tegra124-cpufreq.txt
new file mode 100644
index 0000000..031545a
--- /dev/null
+++ b/Documentation/devicetree/bindings/cpufreq/tegra124-cpufreq.txt
@@ -0,0 +1,42 @@
+Tegra124 CPU frequency scaling driver bindings
+----------------------------------------------
+
+Both required and optional properties listed below must be defined
+under node /cpus/cpu at 0.
+
+Required properties:
+- clocks: Must contain an entry for each entry in clock-names.
+ See ../clocks/clock-bindings.txt for details.
+- clock-names: Must include the following entries:
+ - cpu_g: Clock mux for the fast CPU cluster.
+ - cpu_lp: Clock mux for the low-power CPU cluster.
+ - pll_x: Fast PLL clocksource.
+ - pll_p: Auxiliary PLL used during fast PLL rate changes.
+ - dfll: Fast DFLL clocksource that also automatically scales CPU voltage.
+
+Optional properties:
+- clock-latency: Specify the possible maximum transition latency for clock,
+ in unit of nanoseconds.
+
+Example:
+--------
+cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu at 0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+
+ clocks = <&tegra_car TEGRA124_CLK_CCLK_G>,
+ <&tegra_car TEGRA124_CLK_CCLK_LP>,
+ <&tegra_car TEGRA124_CLK_PLL_X>,
+ <&tegra_car TEGRA124_CLK_PLL_P>,
+ <&dfll>;
+ clock-names = "cpu_g", "cpu_lp", "pll_x", "pll_p", "dfll";
+ clock-latency = <300000>;
+ };
+
+ <...>
+};
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 14/16] cpufreq: Add cpufreq driver for Tegra124
From: Tuomas Tynkkynen @ 2014-07-21 15:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
Add a new cpufreq driver for Tegra124. Instead of using the PLLX as
the CPU clocksource, switch immediately to the DFLL. It allows the use
of higher clock rates, and will automatically scale the CPU voltage as
well. Besides the CPU clocksource switch, we let the cpufreq-cpu0 driver
for all the cpufreq operations.
This driver also relies on the DFLL driver to fill the OPP table for the
CPU0 device, so that the cpufreq-cpu0 driver knows what frequencies to
use.
This driver is a completely independent of the old cpufreq driver
(tegra-cpufreq), which is only used on Tegra20.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- use the cpufreq-cpu0 driver
drivers/cpufreq/Kconfig.arm | 1 +
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/tegra124-cpufreq.c | 169 +++++++++++++++++++++++++++++++++++++
3 files changed, 171 insertions(+)
create mode 100644 drivers/cpufreq/tegra124-cpufreq.c
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 7364a53..df3c73e 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -244,6 +244,7 @@ config ARM_SPEAR_CPUFREQ
config ARM_TEGRA_CPUFREQ
bool "TEGRA CPUFreq support"
depends on ARCH_TEGRA
+ depends on GENERIC_CPUFREQ_CPU0
default y
help
This adds the CPUFreq driver support for TEGRA SOCs.
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index db6d9a2..3437d24 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o
+obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra124-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
##################################################################################
diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
new file mode 100644
index 0000000..65ff428
--- /dev/null
+++ b/drivers/cpufreq/tegra124-cpufreq.c
@@ -0,0 +1,169 @@
+/*
+ * Tegra 124 cpufreq driver
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/cpu.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/types.h>
+
+static struct cpufreq_frequency_table *freq_table;
+
+static struct device *cpu_dev;
+static struct clk *cpu_clk;
+static struct clk *pllp_clk;
+static struct clk *pllx_clk;
+static struct clk *dfll_clk;
+
+static int tegra124_cpu_switch_to_dfll(void)
+{
+ struct clk *original_cpu_clk_parent;
+ unsigned long rate;
+ struct dev_pm_opp *opp;
+ int ret;
+
+ rate = clk_get_rate(cpu_clk);
+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
+ if (IS_ERR(opp))
+ return PTR_ERR(opp);
+
+ ret = clk_set_rate(dfll_clk, rate);
+ if (ret)
+ return ret;
+
+ original_cpu_clk_parent = clk_get_parent(cpu_clk);
+ clk_set_parent(cpu_clk, pllp_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dfll_clk);
+ if (ret)
+ goto out_switch_to_original_parent;
+
+ clk_set_parent(cpu_clk, dfll_clk);
+
+ return 0;
+
+out_switch_to_original_parent:
+ clk_set_parent(cpu_clk, original_cpu_clk_parent);
+
+ return ret;
+}
+
+static struct platform_device_info cpufreq_cpu0_devinfo = {
+ .name = "cpufreq-cpu0",
+};
+
+static int tegra124_cpufreq_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ cpu_dev = get_cpu_device(0);
+ if (!cpu_dev)
+ return -ENODEV;
+
+ cpu_clk = of_clk_get_by_name(cpu_dev->of_node, "cpu_g");
+ if (IS_ERR(cpu_clk))
+ return PTR_ERR(cpu_clk);
+
+ dfll_clk = of_clk_get_by_name(cpu_dev->of_node, "dfll");
+ if (IS_ERR(dfll_clk)) {
+ ret = PTR_ERR(dfll_clk);
+ goto out_put_cpu_clk;
+ }
+
+ pllx_clk = of_clk_get_by_name(cpu_dev->of_node, "pll_x");
+ if (IS_ERR(pllx_clk)) {
+ ret = PTR_ERR(pllx_clk);
+ goto out_put_dfll_clk;
+ }
+
+ pllp_clk = of_clk_get_by_name(cpu_dev->of_node, "pll_p");
+ if (IS_ERR(pllp_clk)) {
+ ret = PTR_ERR(pllp_clk);
+ goto out_put_pllx_clk;
+ }
+
+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
+ if (ret)
+ goto out_put_pllp_clk;
+
+ ret = tegra124_cpu_switch_to_dfll();
+ if (ret)
+ goto out_free_table;
+
+ platform_device_register_full(&cpufreq_cpu0_devinfo);
+
+ return 0;
+
+out_free_table:
+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
+out_put_pllp_clk:
+ clk_put(pllp_clk);
+out_put_pllx_clk:
+ clk_put(pllx_clk);
+out_put_dfll_clk:
+ clk_put(dfll_clk);
+out_put_cpu_clk:
+ clk_put(cpu_clk);
+
+ return ret;
+}
+
+static struct platform_driver tegra124_cpufreq_platdrv = {
+ .driver = {
+ .name = "cpufreq-tegra124",
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra124_cpufreq_probe,
+};
+
+static const struct of_device_id soc_of_matches[] = {
+ { .compatible = "nvidia,tegra124", },
+ {}
+};
+
+static int __init tegra_cpufreq_init(void)
+{
+ int ret;
+ struct platform_device *pdev;
+
+ if (!of_find_matching_node(NULL, soc_of_matches))
+ return -ENODEV;
+
+ ret = platform_driver_register(&tegra124_cpufreq_platdrv);
+ if (ret)
+ return ret;
+
+ pdev = platform_device_register_simple("cpufreq-tegra124", -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ platform_driver_unregister(&tegra124_cpufreq_platdrv);
+ return PTR_ERR(pdev);
+ }
+
+ return 0;
+}
+
+MODULE_AUTHOR("Tuomas Tynkkynen <ttynkkynen@nvidia.com>");
+MODULE_DESCRIPTION("cpufreq driver for nVIDIA Tegra124");
+MODULE_LICENSE("GPLv2");
+module_init(tegra_cpufreq_init);
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 15/16] ARM: tegra: Add entries for cpufreq on Tegra124
From: Tuomas Tynkkynen @ 2014-07-21 15:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
The Tegra124 cpufreq driver relies on certain clocks being present
in the /cpus/cpu at 0 node.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
v2 changes:
- The clock-latency property was added, as cpufreq-cpu0 wants that
arch/arm/boot/dts/tegra124.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index 8ff4332..db51438 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -786,6 +786,15 @@
device_type = "cpu";
compatible = "arm,cortex-a15";
reg = <0>;
+
+ clocks = <&tegra_car TEGRA124_CLK_CCLK_G>,
+ <&tegra_car TEGRA124_CLK_CCLK_LP>,
+ <&tegra_car TEGRA124_CLK_PLL_X>,
+ <&tegra_car TEGRA124_CLK_PLL_P>,
+ <&dfll>;
+ clock-names = "cpu_g", "cpu_lp", "pll_x", "pll_p", "dfll";
+ /* FIXME: what's the actual transition time? */
+ clock-latency = <300000>;
};
cpu at 1 {
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 16/16] ARM: tegra: Update defconfig for tegra124-cpufreq
From: Tuomas Tynkkynen @ 2014-07-21 15:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405957142-19416-1-git-send-email-ttynkkynen@nvidia.com>
The tegra124-cpufreq driver depends on cpufreq-cpu0, so enable
GENERIC_CPUFREQ_CPU0 to get the Tegra driver to build by default.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
---
arch/arm/configs/tegra_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig
index 285c433..afaa97c 100644
--- a/arch/arm/configs/tegra_defconfig
+++ b/arch/arm/configs/tegra_defconfig
@@ -43,6 +43,7 @@ CONFIG_KEXEC=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_STAT_DETAILS=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_GENERIC_CPUFREQ_CPU0=y
CONFIG_CPU_IDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
--
1.8.1.5
^ permalink raw reply related
* [RFC V2] devicetree: Dialog Semiconductor consolidate existing vendor prefixes to standardise on 'dlg'
From: Rob Herring @ 2014-07-21 15:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201407151520.s6FFKcjB009745@swsrvapps-01.diasemi.com>
On Tue, Jul 15, 2014 at 10:03 AM, Opensource [Steve Twiss]
<stwiss.opensource@diasemi.com> wrote:
> From: Opensource [Steve Twiss] <stwiss.opensource@diasemi.com>
>
> This patch series updates the device tree vendor prefix for
> Dialog Semiconductor.
>
> Various methods are currently used throughout the kernel: 'diasemi',
> 'dialog' and 'dlg'. Others have also been suggested.
>
> This patch set aims to consolidate the usage of the vendor prefix to
> use a common standard. The prefix 'dlg' is used.
>
> Here is my working for justifying this change:
>
> ./arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi
> Has the following entry:
> compatible = "dialog,da9063";
> However the DA9063 driver does not support device tree yet so
> it would be safe to rename this.
>
> ./arch/arm/boot/dts/imx53-smd.dts
> Has the following entry:
> compatible = "dialog,da9053", "dialog,da9052";
> However, the existing driver files for DA9053 define a different
> compatible string with the "dlg" prefix. See the entries below.
> None of these would have allowed the "dialog" prefix.
> { .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] },
> { .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] },
> { .compatible = "dlg,da9053-ba", .data = &da9052_i2c_id[2] },
> { .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] },
> { .compatible = "dlg,da9053-bc", .data = &da9052_i2c_id[4] },
> In this particular case the change for DA9053 did not match up
> with the expected compatibility strings and therefore I have
> retained the more general "dlg,da9053" because I do not know
> which silicon variant (-aa, -ba, -bb, or -bc) is the correct
> one to use.
>
> ./devicetree/bindings/i2c/trivial-devices.txt
> Has the following entry:
> dialog,da9053 DA9053: flexible system level PMIC with multicore support
> Instead of depreciating this "dialog" line I am just replacing it
> with a "dlg" because the existing driver DA9053 does not support
> the dialog keyword.
>
> ./drivers/mfd/da9055-core.c
> Has the following entries for the mfd cells
> .of_compatible = "dialog,da9055-gpio", etc...
> In this case, the driver does not actually pass in any platform data
> to any of the mfd cells and so they are not actually used
> yet in the driver. Nobody else references this information.
>
> ./devicetree/bindings/regulator/da9210.txt
> Has the following two entries in the binding file:
> - compatible: must be "diasemi,da9210"
> compatible = "diasemi,da9210";
> However the DA9210 driver does not support device tree.
>
> ./arch/arm/boot/dts/r8a7790-lager.dts
> ./arch/arm/boot/dts/r8a7791-koelsch.dts
> These two files have the following entries:
> compatible = "diasemi,da9210";
> These both reference the "diasemi,da9210" but the device
> driver does not support device tree
>
> The remaining files in the kernel I have found correctly references
> the driver files compatibility information and so did not need to
> be changed.
>
> ./devicetree/bindings/mfd/da9052-i2c.txt
> - compatible : Should be "dlg,da9052", "dlg,da9053-aa",
> "dlg,da9053-ab", or "dlg,da9053-bb"
>
> ./devicetree/bindings/mfd/da9055.txt
> compatible = "dlg,da9055-pmic";
>
> ./arch/arm/boot/dts/imx53-voipac-dmm-668.dtsi
> compatible = "dlg,da9053-aa", "dlg,da9052";
>
> ./arch/arm/boot/dts/imx53-qsb.dts
> compatible = "dlg,da9053-aa", "dlg,da9052";
>
> ./devicetree/bindings/sound/da9055.txt
> - compatible: "dlg,da9055-codec"
> compatible = "dlg,da9055-codec";
>
>
> Signed-off-by: Opensource [Steve Twiss] <stwiss.opensource@diasemi.com>
Can you format your email normally and drop "Opensource" and the brackets.
I'm okay with this, but it also needs acks from i.MX and SHMobile
folks as it could break out of tree users.
Acked-by: Rob Herring <robh@kernel.org>
Rob
> ---
> Checks performed with linux-next/next-20140715/scripts/checkpatch.pl
> trivial-devices.txt total: 0 errors, 0 warnings, 89 lines checked
> da9210.txt total: 0 errors, 0 warnings, 21 lines checked
> vendor-prefixes.txt total: 0 errors, 0 warnings, 149 lines checked
> imx53-smd.dts total: 0 errors, 2 warnings, 279 lines checked
> imx6qdl-phytec-pfla02.dtsi total: 0 errors, 2 warnings, 357 lines checked
> r8a7790-lager.dts total: 0 errors, 3 warnings, 403 lines checked
> r8a7791-koelsch.dts total: 0 errors, 4 warnings, 461 lines checked
> da9055-core.c total: 0 errors, 0 warnings, 428 lines checked
>
> This e-mail is in response to the previous threads here:
> https://lkml.org/lkml/2014/6/11/262
> http://comments.gmane.org/gmane.linux.ports.arm.kernel/341358
>
> Changes since RFC V1
> - addition of changes to DTS files referencing "diasemi,da9210"
> arch/arm/boot/dts/r8a7790-lager.dts
> arch/arm/boot/dts/r8a7791-koelsch.dts
>
> This RFC V2 does not answer the response to RFC V1 from Mark Brown:
> http://www.spinics.net/lists/arm-kernel/msg347615.html
>
> This patch applies against linux-next and next-20140715
>
> Regards,
> Steve Twiss, Dialog Semiconductor Ltd.
>
>
>
> .../devicetree/bindings/i2c/trivial-devices.txt | 2 +-
> .../devicetree/bindings/regulator/da9210.txt | 4 ++--
> .../devicetree/bindings/vendor-prefixes.txt | 1 +
> arch/arm/boot/dts/imx53-smd.dts | 2 +-
> arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi | 2 +-
> arch/arm/boot/dts/r8a7790-lager.dts | 2 +-
> arch/arm/boot/dts/r8a7791-koelsch.dts | 2 +-
> drivers/mfd/da9055-core.c | 26 +++++++++++-----------
> 8 files changed, 21 insertions(+), 20 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> index 37803eb..a02b8a1 100644
> --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> @@ -44,7 +44,7 @@ dallas,ds1775 Tiny Digital Thermometer and Thermostat
> dallas,ds3232 Extremely Accurate I?C RTC with Integrated Crystal and SRAM
> dallas,ds4510 CPU Supervisor with Nonvolatile Memory and Programmable I/O
> dallas,ds75 Digital Thermometer and Thermostat
> -dialog,da9053 DA9053: flexible system level PMIC with multicore support
> +dlg,da9053 DA9053: flexible system level PMIC with multicore support
> epson,rx8025 High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
> epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
> fsl,mag3110 MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
> diff --git a/Documentation/devicetree/bindings/regulator/da9210.txt b/Documentation/devicetree/bindings/regulator/da9210.txt
> index f120f22..3297c53 100644
> --- a/Documentation/devicetree/bindings/regulator/da9210.txt
> +++ b/Documentation/devicetree/bindings/regulator/da9210.txt
> @@ -2,7 +2,7 @@
>
> Required properties:
>
> -- compatible: must be "diasemi,da9210"
> +- compatible: must be "dlg,da9210"
> - reg: the i2c slave address of the regulator. It should be 0x68.
>
> Any standard regulator properties can be used to configure the single da9210
> @@ -11,7 +11,7 @@ DCDC.
> Example:
>
> da9210 at 68 {
> - compatible = "diasemi,da9210";
> + compatible = "dlg,da9210";
> reg = <0x68>;
>
> regulator-min-microvolt = <900000>;
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index 5d27e5a..35a833e 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -37,6 +37,7 @@ dallas Maxim Integrated Products (formerly Dallas Semiconductor)
> davicom DAVICOM Semiconductor, Inc.
> denx Denx Software Engineering
> digi Digi International Inc.
> +dlg Dialog Semiconductor
> dlink D-Link Corporation
> dmo Data Modul AG
> ebv EBV Elektronik
> diff --git a/arch/arm/boot/dts/imx53-smd.dts b/arch/arm/boot/dts/imx53-smd.dts
> index 5ec1590..1d32557 100644
> --- a/arch/arm/boot/dts/imx53-smd.dts
> +++ b/arch/arm/boot/dts/imx53-smd.dts
> @@ -265,7 +265,7 @@
> };
>
> pmic: dialog at 48 {
> - compatible = "dialog,da9053", "dialog,da9052";
> + compatible = "dlg,da9053", "dlg,da9052";
> reg = <0x48>;
> };
> };
> diff --git a/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi b/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi
> index 2694aa8..0e50bb0 100644
> --- a/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi
> +++ b/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi
> @@ -83,7 +83,7 @@
> };
>
> pmic at 58 {
> - compatible = "dialog,da9063";
> + compatible = "dlg,da9063";
> reg = <0x58>;
> interrupt-parent = <&gpio4>;
> interrupts = <17 0x8>; /* active-low GPIO4_17 */
> diff --git a/arch/arm/boot/dts/r8a7790-lager.dts b/arch/arm/boot/dts/r8a7790-lager.dts
> index 856b423..e3db105 100644
> --- a/arch/arm/boot/dts/r8a7790-lager.dts
> +++ b/arch/arm/boot/dts/r8a7790-lager.dts
> @@ -374,7 +374,7 @@
> status = "okay";
>
> vdd_dvfs: regulator at 68 {
> - compatible = "diasemi,da9210";
> + compatible = "dlg,da9210";
> reg = <0x68>;
>
> regulator-min-microvolt = <1000000>;
> diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts
> index 23486c0..33b9532 100644
> --- a/arch/arm/boot/dts/r8a7791-koelsch.dts
> +++ b/arch/arm/boot/dts/r8a7791-koelsch.dts
> @@ -426,7 +426,7 @@
> clock-frequency = <100000>;
>
> vdd_dvfs: regulator at 68 {
> - compatible = "diasemi,da9210";
> + compatible = "dlg,da9210";
> reg = <0x68>;
>
> regulator-min-microvolt = <1000000>;
> diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c
> index caf8dcf..b4d920c 100644
> --- a/drivers/mfd/da9055-core.c
> +++ b/drivers/mfd/da9055-core.c
> @@ -296,73 +296,73 @@ static struct resource da9055_ld05_6_resource = {
>
> static const struct mfd_cell da9055_devs[] = {
> {
> - .of_compatible = "dialog,da9055-gpio",
> + .of_compatible = "dlg,da9055-gpio",
> .name = "da9055-gpio",
> },
> {
> - .of_compatible = "dialog,da9055-regulator",
> + .of_compatible = "dlg,da9055-regulator",
> .name = "da9055-regulator",
> .id = 1,
> },
> {
> - .of_compatible = "dialog,da9055-regulator",
> + .of_compatible = "dlg,da9055-regulator",
> .name = "da9055-regulator",
> .id = 2,
> },
> {
> - .of_compatible = "dialog,da9055-regulator",
> + .of_compatible = "dlg,da9055-regulator",
> .name = "da9055-regulator",
> .id = 3,
> },
> {
> - .of_compatible = "dialog,da9055-regulator",
> + .of_compatible = "dlg,da9055-regulator",
> .name = "da9055-regulator",
> .id = 4,
> },
> {
> - .of_compatible = "dialog,da9055-regulator",
> + .of_compatible = "dlg,da9055-regulator",
> .name = "da9055-regulator",
> .id = 5,
> },
> {
> - .of_compatible = "dialog,da9055-regulator",
> + .of_compatible = "dlg,da9055-regulator",
> .name = "da9055-regulator",
> .id = 6,
> },
> {
> - .of_compatible = "dialog,da9055-regulator",
> + .of_compatible = "dlg,da9055-regulator",
> .name = "da9055-regulator",
> .id = 7,
> .resources = &da9055_ld05_6_resource,
> .num_resources = 1,
> },
> {
> - .of_compatible = "dialog,da9055-regulator",
> + .of_compatible = "dlg,da9055-regulator",
> .name = "da9055-regulator",
> .resources = &da9055_ld05_6_resource,
> .num_resources = 1,
> .id = 8,
> },
> {
> - .of_compatible = "dialog,da9055-onkey",
> + .of_compatible = "dlg,da9055-onkey",
> .name = "da9055-onkey",
> .resources = &da9055_onkey_resource,
> .num_resources = 1,
> },
> {
> - .of_compatible = "dialog,da9055-rtc",
> + .of_compatible = "dlg,da9055-rtc",
> .name = "da9055-rtc",
> .resources = da9055_rtc_resource,
> .num_resources = ARRAY_SIZE(da9055_rtc_resource),
> },
> {
> - .of_compatible = "dialog,da9055-hwmon",
> + .of_compatible = "dlg,da9055-hwmon",
> .name = "da9055-hwmon",
> .resources = &da9055_hwmon_resource,
> .num_resources = 1,
> },
> {
> - .of_compatible = "dialog,da9055-watchdog",
> + .of_compatible = "dlg,da9055-watchdog",
> .name = "da9055-watchdog",
> },
> };
> --
> end-of-patch for RFC V2
>
^ permalink raw reply
* [PATCH] spi: omap2-mcspi: fix blatant abuse of the resource subsystem
From: Mark Brown @ 2014-07-21 15:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140721144104.5e37f1a0@ipc1.ka-ro>
On Mon, Jul 21, 2014 at 02:41:04PM +0200, Lothar Wa?mann wrote:
> Mark Brown wrote:
> > Just to be clear the issue is with the presentation of your change, it
> > is not appropriate to provide a changelog which consists mainly of
> > widely directed personal insults especially given that it omits basic
> > technical content.
> I understood that, but I feel incapable to come up with a reasonable
> changelog for this.
Just describe in a specific, technical fashion what the problem is and
what the fix does without reference to personal qualities of the various
parties involved in the code being the way it is.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140721/585a0d12/attachment.sig>
^ permalink raw reply
* [PATCH 00/14] arm64: eBPF JIT compiler
From: Alexei Starovoitov @ 2014-07-21 15:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140721091623.GA16122@arm.com>
On Mon, Jul 21, 2014 at 2:16 AM, Will Deacon <will.deacon@arm.com> wrote:
> Hello,
>
> On Fri, Jul 18, 2014 at 07:28:06PM +0100, Zi Shen Lim wrote:
>> This series implements eBPF JIT compiler for arm64.
>> Please see [14/14] for change log.
>>
>> Patches [1-13/14] implement code generation functions.
>> Patch [14/14] implements the actual eBPF JIT compiler.
>>
>> Many thanks to everyone who's reviewed the code from
>> RFCv1->RFCv3, especially Alexei for BPF bits, and Will
>> for ARM64 codegen bits :)
>>
>> BTW, I'm happy to maintain arch/arm64/net (i.e. arm64 BPF bits).
>> Should I add a patch updating MAINTAINERS as Patch 15?
>
> I don't think that's necessary at the moment, but if we start seeing an
> influx of patches to arch/arm64/net, then that could make sense in the
> future.
>
>> This series requires a patch that exports a function
>> from net/core (resulting from RFCv1 discussion). This patch
>> has been merged into net-next @ 9f12fbe603f7
>> ("net: filter: move load_pointer() into filter.h").
>>
>> This series applies against net-next and is tested working
>> with lib/test_bpf on ARMv8 Foundation Model.
>
> Looks like it works on my Juno board too, so:
>
> Acked-by: Will Deacon <will.deacon@arm.com>
>
> for the series.
>
> It's a bit late for 3.17 now, so I guess we'll queue this for 3.18 (which
> also means the dependency on -next isn't an issue). Perhaps you could repost
> around -rc3?
Thanks for testing! Nice to see it working on real hw.
I'm not sure why you're proposing a 4+ week delay. The patches
will rot instead of getting used and tested. Imo it makes sense to
get them into net-next now for 3.17.
JIT is disabled by sysctl by default anyway.
^ permalink raw reply
* a case for a common efuse API?
From: Stephen Warren @ 2014-07-21 15:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140709114907.GI23218@tbergstrom-lnx.Nvidia.com>
On 07/09/2014 05:49 AM, Peter De Schrijver wrote:
> On Tue, Jul 08, 2014 at 10:00:23PM +0200, Stephen Boyd wrote:
>>
>> I added Tegra folks because I see that on Tegra this hardware is exposed
>> via an SoC specific API, tegra_fuse_readl(), and an associated driver in
>> drivers/misc/fuse/tegra/. Unfortunately I don't see any users of the API
>> outside of the speedo code in the same directory and the sysfs bin
>> attribute that may or may not be in use by some userspace code.
>>
>
> The SATA driver by Mikko Perttunen uses it. The driver hasn't been merged
> though. There's some more upcoming work which makes use of it.
> I don't think we can standardize much of an API for this. The data is
> SoC specific, so the user will always need to have some SoC specific
> knowledge on how to use it.
I think we could have a generic "read fuse X" API. The only thing that'd
be chip-specific is the value of "X". That could be hard-coded into the
client drivers, or perhaps represented in a DT propery e.g.
#fuse-cells/xxx-fuses. That said, I wonder what benefit we'd get from
such a common API. I suppose if any IP block was to be re-used in
completely different SoC families, it'd isolate the driver from having
to call different functions to read fuses on different SoC families, but
that doesn't seem to happen in practice yet, and the issue could be
solved later if needed.
It'd certainly be hard to create any higher-layer semantic API here,
since different SoCs store completely different sets of data in fuses,
so there would be little point in a common "read the MAC address from
the fuses" API, since that data wouldn't exist in fuses on many SoCs.
> In some cases we need it very early (eg. to
> determine the correct Tegra20 revision). On Tegra20 we can't use the CPU
> to read the fuses due to a hw bug, we have to use DMA, which means the
> transaction becomes blocking and could fail due to lack of resources.
Yes, any such API would become "least common-denominator" or similar;
i.e. any restriction imposed by any SoC would then affect how the API
works on any other SoC.
^ permalink raw reply
* [PATCH 0/9] usb: musb: several bugfixes for the musb driver
From: Ezequiel Garcia @ 2014-07-21 15:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140721151140.GG6852@saruman.home>
On 21 Jul 10:11 AM, Felipe Balbi wrote:
> On Mon, Jul 21, 2014 at 09:34:30AM +0200, Lothar Wa?mann wrote:
> > Hi,
> >
> > Felipe Balbi wrote:
> > > Hi,
> > >
> > > On Fri, Jul 18, 2014 at 01:16:36PM -0300, Ezequiel Garcia wrote:
> > > > Hi Lothar,
> > > >
> > > > On 18 Jul 11:31 AM, Lothar Wa?mann wrote:
> > > > > The first three patches do some source code cleanup in the files that
> > > > > are modified in the subsequent patches.
> > > > >
> > > >
> > > > I've applied patches 4 and 9 on a recent -next, after fixing a conflict
> > > > due to patch 3 ("usb: musb_am335x: source cleanup"):
> > > >
> > > > > Patch 4 carries the proper fix reported in commit:
> > > > > 7adb5c876e9c ("usb: musb: Fix panic upon musb_am335x module removal")
> > > > >
> > > > > Patch 9 reinstates module unloading support for the musb_am335x driver
> > > > > which was disabled due to a false failure analysis
> > > > >
> > > >
> > > > For these two patches, Tested-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
> > > >
> > > > Tested on a beaglebone with a mass storage USB device, module load/unload
> > > > works without issues. The module_get/put in the phy is now preventing the
> > > > musb_am335x driver unload, which seems to be the real cause of the issue
> > > > I reported. Thanks for providing a proper fix!
> > >
> > > I don't that's a good fix. The problem is that even after am335x
> > > removing all its child, someone else tried to release the PHY. That
> > > ordering is the one that needs to be fixed. Doing a module_get on the
> > > parent device looks like a hack to me.
> > >
> > No. It guarantees that the module cannot be unloaded when its resources
> > are still in use!
>
> it's still a hack. You have a child incrementing the usage count on its
> parent just because a sibbling isn't doing the right thing.
>
How about having the musb_am335x (glue driver) call request_module and
module_get on the phy-am335x? Wouldn't this be a cleaner approach?
I haven't checked if this possible, though.
--
Ezequiel Garcia, VanguardiaSur
www.vanguardiasur.com.ar
^ permalink raw reply
* [GIT PULL 2/3] ARM: tegra: move fuse code out of arch/arm
From: Catalin Marinas @ 2014-07-21 15:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <53CD2C58.6060601@wwwdotorg.org>
On Mon, Jul 21, 2014 at 09:06:00AM -0600, Stephen Warren wrote:
> On 07/17/2014 11:33 PM, Olof Johansson wrote:
> > On Thu, Jul 17, 2014 at 7:45 PM, Stephen Warren <swarren@wwwdotorg.org> wrote:
> >> On 07/08/2014 11:47 AM, Olof Johansson wrote:
> >>> On Tue, Jul 8, 2014 at 6:43 AM, Peter De Schrijver
> >>> <pdeschrijver@nvidia.com> wrote:
> >>>> On Mon, Jul 07, 2014 at 02:44:17AM +0200, Olof Johansson wrote:
> >>>>> On Mon, Jun 23, 2014 at 03:23:45PM -0600, Stephen Warren wrote:
> >>>>>> This branch moves code related to the Tegra fuses out of arch/arm and
> >>>>>> into a centralized location which could be shared with ARM64. It also
> >>>>>> adds support for reading the fuse data through sysfs.
> >>>>>
> >>>>> The new/moved misc driver isn't acked by any misc maintainer, so I can't
> >>>>> take this branch.
> >>>>>
> >>>>> I saw no indication from searching the mailing list of that either,
> >>>>> so it wasn't just a missed acked-by.
> >>>>>
> >>>>> I wonder if this code should go under drivers/soc/ instead?
> >>>>
> >>>> It's modelled after sunxi_sid.c which lives in drivers/misc/eeprom/.
> >>>> Originally this driver was also in drivers/misc/eeprom/, but Stephen objected
> >>>> and therefore it was moved to drivers/misc/fuse. I think that's the right
> >>>> place still.
> >>>
> >>> I disagree, I think this belongs under drivers/soc. Especially since
> >>> you're adding dependencies on this misc driver from other parts of the
> >>> kernel / other drivers.
> >>>
> >>> I also don't like seeing init calls form platform code down into
> >>> drivers/misc like you're adding here. Can you please look at doing
> >>> that as a regular init call setup?
> >>
> >> I strongly disagree with using init calls for this kind of thing. There
> >> are ordering dependencies between the initialization code that can only
> >> be sanely managed by explicitly calling functions in a particular order;
> >> there's simply no way to manage this using initcalls. This is exactly
> >> why the hooks in the ARM machine descriptors exist...
> >
> > Right, but there are non on 64-bit, so you need to solve it for there
> > anyway. And once it's solved there, you might as well keep it common
> > with 32-bit.
>
> My assertion is that we should just do it directly on 64-bit too.
> There's no reason for arm64 to deviate from what arch/arm does already.
On arm64, I really want to get away from any SoC specific early
initcall. One of the main reason is for things like SCU, interconnects,
system cache configurations (even certain clocks) to be enabled in
firmware before Linux starts (it's an education process but that's a way
for example to prevent people sending patches to enable SoC coherency
because they haven't thought about it before upstreaming).
It would be nice to be able to initialise SoC stuff at device_initcall()
level (and even keep such code as modules in initramfs) but one of the
problems we have is dependency on clocks (and the clock model which
doesn't follow the device/driver model). The of_platform_populate() is
called at arch_initcall_sync (after arch_initcall to still allow some
SoC code, if needed, to run at arch_initcall).
We have a similar issue with arm64 vexpress (well, just on the model)
where vexpress_sysreg_init() is a core_initcall (should be fine as
arch_initcall) as it needs to be done before of_platform_populate().
Pawel on cc should know more of the history here.
I recall there were also some discussions about a SoC driver model which
hangs off the top compatible string in the DT (e.g. "arm,vexpress") and
allow (minimal) code to be run slightly earlier, though still not
earlier than arch_initcall.
--
Catalin
^ permalink raw reply
* [PATCH 0/9] usb: musb: several bugfixes for the musb driver
From: Felipe Balbi @ 2014-07-21 15:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140721155352.GA20226@arch.cereza>
On Mon, Jul 21, 2014 at 12:53:52PM -0300, Ezequiel Garcia wrote:
> On 21 Jul 10:11 AM, Felipe Balbi wrote:
> > On Mon, Jul 21, 2014 at 09:34:30AM +0200, Lothar Wa?mann wrote:
> > > Hi,
> > >
> > > Felipe Balbi wrote:
> > > > Hi,
> > > >
> > > > On Fri, Jul 18, 2014 at 01:16:36PM -0300, Ezequiel Garcia wrote:
> > > > > Hi Lothar,
> > > > >
> > > > > On 18 Jul 11:31 AM, Lothar Wa?mann wrote:
> > > > > > The first three patches do some source code cleanup in the files that
> > > > > > are modified in the subsequent patches.
> > > > > >
> > > > >
> > > > > I've applied patches 4 and 9 on a recent -next, after fixing a conflict
> > > > > due to patch 3 ("usb: musb_am335x: source cleanup"):
> > > > >
> > > > > > Patch 4 carries the proper fix reported in commit:
> > > > > > 7adb5c876e9c ("usb: musb: Fix panic upon musb_am335x module removal")
> > > > > >
> > > > > > Patch 9 reinstates module unloading support for the musb_am335x driver
> > > > > > which was disabled due to a false failure analysis
> > > > > >
> > > > >
> > > > > For these two patches, Tested-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
> > > > >
> > > > > Tested on a beaglebone with a mass storage USB device, module load/unload
> > > > > works without issues. The module_get/put in the phy is now preventing the
> > > > > musb_am335x driver unload, which seems to be the real cause of the issue
> > > > > I reported. Thanks for providing a proper fix!
> > > >
> > > > I don't that's a good fix. The problem is that even after am335x
> > > > removing all its child, someone else tried to release the PHY. That
> > > > ordering is the one that needs to be fixed. Doing a module_get on the
> > > > parent device looks like a hack to me.
> > > >
> > > No. It guarantees that the module cannot be unloaded when its resources
> > > are still in use!
> >
> > it's still a hack. You have a child incrementing the usage count on its
> > parent just because a sibbling isn't doing the right thing.
> >
>
> How about having the musb_am335x (glue driver) call request_module and
> module_get on the phy-am335x? Wouldn't this be a cleaner approach?
>
> I haven't checked if this possible, though.
at most, you would have the phy layer do that so that all PHYs get usage
counter incremented when they're in use. We can't have this 'fixed' for
MUSB only.
--
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140721/4de6e2bb/attachment-0001.sig>
^ permalink raw reply
* [PATCH] exynos: Support big endian mode in secondary_startup
From: Mark Brown @ 2014-07-21 16:01 UTC (permalink / raw)
To: linux-arm-kernel
From: Victor Kamensky <victor.kamensky@linaro.org>
Exynos processors generally operate in little endian mode so their
bootloader and ROM will almost always operate in little endian mode.
This means that if a big endian kernel is run it must switch the CPU
into big endian mode after gaining control.
The generic secondary_startup that is called from exynos specific
secondary startup code will do the switch, but we need it to do earlier
because exynos specific secondary_startup code which runs first also
works with data that is big endian when the kernel is compiled for big
endian.
[Rewrote commit message. -- broonie]
Signed-off-by: Victor Kamensky <victor.kamensky@linaro.org>
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm/mach-exynos/headsmp.S | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/mach-exynos/headsmp.S b/arch/arm/mach-exynos/headsmp.S
index b54f9701e421..ac8364efb985 100644
--- a/arch/arm/mach-exynos/headsmp.S
+++ b/arch/arm/mach-exynos/headsmp.S
@@ -18,6 +18,11 @@
* ready for them to initialise.
*/
ENTRY(exynos4_secondary_startup)
+ /*
+ * ROM code operates in little endian mode, when we get control we
+ * need to switch it to big endian mode.
+ */
+ARM_BE8(setend be)
mrc p15, 0, r0, c0, c0, 5
and r0, r0, #15
adr r4, 1f
--
2.0.1
^ permalink raw reply related
* [PATCH v6 0/7] ARM generic idle states
From: Lorenzo Pieralisi @ 2014-07-21 16:06 UTC (permalink / raw)
To: linux-arm-kernel
This patch is v6 of a previous posting:
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-June/266488.html
Patchset has been tested on:
- Juno ARM64 platform and Foundation v8 models (based on Trusted Firmware
PSCI implementation available at [1])
# Patches to enable idle states on Juno and Foundation models can be made
available, current dts are not in the mainline kernel
- TC2
- Compile tested on Samsung Exynos
Patch for Exynos CPUidle code depends on patch [2].
[1] https://github.com/ARM-software/arm-trusted-firmware
[2] http://lists.infradead.org/pipermail/linux-arm-kernel/2014-July/274179.html
Changes in v6:
- Added ARM64 CPU init idle to CPU operations
- Removed power-rank property; sorting depends on the cpu-idle-states phandle
list order now
- Added local-timer-stop flag
- Removed useless TC2 and Exynos DT idle state entry-method bindings
- Removed RTSM dts update
- Updated PSCI suspend parameter and DT bindings
- Updated PSCI parsing code
- Rebased against 3.16-rc6
Changes in v5:
- Added power-rank property to implement state sorting, following a number
of on/off list review comments
- Added timer retained bool property
- Ported TC2 big.LITTLE and Exynos drivers to DT initialization
- Renamed s/OF/dt/ throughout the patch
- Incorporated review comments and list discussions in the idle states
bindings documents
- Rebased against 3.16-rc2
Changes in v4:
- States sorting using exit-latency
- Added cosmetic review comments
- Dropped RFC
- Rebased against 3.15
Changes in v3:
- Streamlined the idle states bindings and added them to the series
http://www.spinics.net/lists/arm-kernel/msg316299.html
- Sorting states through min-residency+exit-latency
- Added debug strings formatting
- Reworded min-residency-us idle state property
- Removed power-domain properties from idle states waiting for code
examples requiring them to be defined
Changes in v2:
- Moved OF parsing code to drivers/cpuidle
- Improved states detection and sorting through linked list
- Split code in generic and ARM64 specific bits
- Moved idle enter function into ARM64 idle driver
- Refactored PSCI idle states register function
- Renamed suspend operations and moved detection to ARM64 idle driver
- Changed the way CPUIDLE_FLAG_TIMER_STOP is handled
- Simplified idle state nodes parsing since according to the latest
bindings idle state nodes are a flat list, not hierarchical anymore
- Used min-residency-us to sort the states, to be further discussed
Idle states on most ARM platforms can be characterized by a set of
parameters that are platform agnostic and describe the HW idle states
features. So far, CPU idle drivers for ARM platforms required the definition
of parameters through static tables, duplicating control data for different
platforms. Moreover, the lack of standardization on firmware interfaces
hampered any standardization effort, resulting in CPU idle drivers for ARM
platforms containing duplicated code and platform specific power down routines.
The introduction of the PSCI firmware interface, and more in general, well
defined suspend back-ends, allows the definition of generic idle states and
the respective kernel infrastructure to support them.
Building on top of DT idle states bindings, that standardize idle states
parameters and corresponding suspend back-ends, this patchset provides code
that parses DT idle states nodes and builds at run-time the control data
infrastructure required by the ARM CPU idle drivers.
Idle states define an entry method (eg PSCI), that requires the respective
ARM64 kernel back-end to be invoked to initialize idle states parameters, so
that when the idle driver executes the back-end specific entry method a table
look-up can be carried out to retrieve the corresponding idle state parameter.
On legacy ARM platforms, the DT idle states are just used to initialize
states data.
Lorenzo Pieralisi (7):
Documentation: arm: define DT idle states bindings
drivers: cpuidle: implement DT based idle states infrastructure
arm64: kernel: introduce cpu_init_idle CPU operation
arm64: add PSCI CPU_SUSPEND based cpu_suspend support
drivers: cpuidle: CPU idle ARM64 driver
drivers: cpuidle: initialize big.LITTLE driver through DT
drivers: cpuidle: initialize Exynos driver through DT
Documentation/devicetree/bindings/arm/cpus.txt | 8 +
.../devicetree/bindings/arm/idle-states.txt | 679 +++++++++++++++++++++
Documentation/devicetree/bindings/arm/psci.txt | 14 +-
arch/arm/boot/dts/exynos4210.dtsi | 11 +
arch/arm/boot/dts/exynos5250.dtsi | 11 +
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 23 +
arch/arm64/include/asm/cpu_ops.h | 1 +
arch/arm64/include/asm/cpuidle.h | 13 +
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/cpuidle.c | 27 +
arch/arm64/kernel/psci.c | 89 +++
drivers/cpuidle/Kconfig | 13 +
drivers/cpuidle/Kconfig.arm | 2 +
drivers/cpuidle/Kconfig.arm64 | 14 +
drivers/cpuidle/Makefile | 5 +
drivers/cpuidle/cpuidle-arm64.c | 128 ++++
drivers/cpuidle/cpuidle-big_little.c | 43 +-
drivers/cpuidle/cpuidle-exynos.c | 37 +-
drivers/cpuidle/dt_idle_states.c | 138 +++++
drivers/cpuidle/dt_idle_states.h | 5 +
20 files changed, 1227 insertions(+), 35 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/idle-states.txt
create mode 100644 arch/arm64/include/asm/cpuidle.h
create mode 100644 arch/arm64/kernel/cpuidle.c
create mode 100644 drivers/cpuidle/Kconfig.arm64
create mode 100644 drivers/cpuidle/cpuidle-arm64.c
create mode 100644 drivers/cpuidle/dt_idle_states.c
create mode 100644 drivers/cpuidle/dt_idle_states.h
--
1.9.1
^ permalink raw reply
* [PATCH v6 1/7] Documentation: arm: define DT idle states bindings
From: Lorenzo Pieralisi @ 2014-07-21 16:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405958786-17243-1-git-send-email-lorenzo.pieralisi@arm.com>
ARM based platforms implement a variety of power management schemes that
allow processors to enter idle states at run-time.
The parameters defining these idle states vary on a per-platform basis forcing
the OS to hardcode the state parameters in platform specific static tables
whose size grows as the number of platforms supported in the kernel increases
and hampers device drivers standardization.
Therefore, this patch aims at standardizing idle state device tree bindings
for ARM platforms. Bindings define idle state parameters inclusive of entry
methods and state latencies, to allow operating systems to retrieve the
configuration entries from the device tree and initialize the related power
management drivers, paving the way for common code in the kernel to deal with
idle states and removing the need for static data in current and previous
kernel versions.
ARM64 platforms require the DT to define an entry-method property
for idle states.
On system implementing PSCI as an enable-method to enter low-power
states the PSCI CPU suspend method requires the power_state parameter to
be passed to the PSCI CPU suspend function.
This parameter is specific to a power state and platform specific,
therefore must be provided by firmware to the OS in order to enable
proper call sequence.
Thus, this patch also adds a property in the PSCI bindings that
describes how the PSCI CPU suspend power_state parameter should be
defined in DT in all device nodes that rely on PSCI CPU suspend method usage.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
Documentation/devicetree/bindings/arm/cpus.txt | 8 +
.../devicetree/bindings/arm/idle-states.txt | 679 +++++++++++++++++++++
Documentation/devicetree/bindings/arm/psci.txt | 14 +-
3 files changed, 700 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/arm/idle-states.txt
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
index 1fe72a0..a44d4fd 100644
--- a/Documentation/devicetree/bindings/arm/cpus.txt
+++ b/Documentation/devicetree/bindings/arm/cpus.txt
@@ -215,6 +215,12 @@ nodes to be present and contain the properties described below.
Value type: <phandle>
Definition: Specifies the ACC[2] node associated with this CPU.
+ - cpu-idle-states
+ Usage: Optional
+ Value type: <prop-encoded-array>
+ Definition:
+ # List of phandles to idle state nodes supported
+ by this cpu [3].
Example 1 (dual-cluster big.LITTLE system 32-bit):
@@ -411,3 +417,5 @@ cpus {
--
[1] arm/msm/qcom,saw2.txt
[2] arm/msm/qcom,kpss-acc.txt
+[3] ARM Linux kernel documentation - idle states bindings
+ Documentation/devicetree/bindings/arm/idle-states.txt
diff --git a/Documentation/devicetree/bindings/arm/idle-states.txt b/Documentation/devicetree/bindings/arm/idle-states.txt
new file mode 100644
index 0000000..37375c7
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/idle-states.txt
@@ -0,0 +1,679 @@
+==========================================
+ARM idle states binding description
+==========================================
+
+==========================================
+1 - Introduction
+==========================================
+
+ARM systems contain HW capable of managing power consumption dynamically,
+where cores can be put in different low-power states (ranging from simple
+wfi to power gating) according to OS PM policies. The CPU states representing
+the range of dynamic idle states that a processor can enter at run-time, can be
+specified through device tree bindings representing the parameters required
+to enter/exit specific idle states on a given processor.
+
+According to the Server Base System Architecture document (SBSA, [3]), the
+power states an ARM CPU can be put into are identified by the following list:
+
+- Running
+- Idle_standby
+- Idle_retention
+- Sleep
+- Off
+
+The power states described in the SBSA document define the basic CPU states on
+top of which ARM platforms implement power management schemes that allow an OS
+PM implementation to put the processor in different idle states (which include
+states listed above; "off" state is not an idle state since it does not have
+wake-up capabilities, hence it is not considered in this document).
+
+Idle state parameters (eg entry latency) are platform specific and need to be
+characterized with bindings that provide the required information to OS PM
+code so that it can build the required tables and use them at runtime.
+
+The device tree binding definition for ARM idle states is the subject of this
+document.
+
+===========================================
+2 - idle-states definitions
+===========================================
+
+Idle states are characterized for a specific system through a set of
+timing and energy related properties, that underline the HW behaviour
+triggered upon idle states entry and exit.
+
+The following diagram depicts the CPU execution phases and related timing
+properties required to enter and exit an idle state:
+
+..__[EXEC]__|__[PREP]__|__[ENTRY]__|__[IDLE]__|__[EXIT]__|__[EXEC]__..
+ | | | | |
+
+ |<------ entry ------->|
+ | latency |
+ |<- exit ->|
+ | latency |
+ |<-------- min-residency -------->|
+ |<------- wakeup-latency ------->|
+
+ Diagram 1: CPU idle state execution phases
+
+EXEC: Normal CPU execution.
+
+PREP: Preparation phase before committing the hardware to idle mode
+ like cache flushing. This is abortable on pending wake-up
+ event conditions. The abort latency is assumed to be negligible
+ (i.e. less than the ENTRY + EXIT duration). If aborted, CPU
+ goes back to EXEC. This phase is optional. If not abortable,
+ this should be included in the ENTRY phase instead.
+
+ENTRY: The hardware is committed to idle mode. This period must run
+ to completion up to IDLE before anything else can happen.
+
+IDLE: This is the actual energy-saving idle period. This may last
+ between 0 and infinite time, until a wake-up event occurs.
+
+EXIT: Period during which the CPU is brought back to operational
+ mode (EXEC).
+
+entry-latency: Worst case latency required to enter the idle state. The
+exit-latency may be guaranteed only after entry-latency has passed.
+
+min-residency: Minimum period, including preparation and entry, for a given
+idle state to be worthwhile energywise.
+
+wakeup-latency: Maximum delay between the signaling of a wake-up event and the
+CPU being able to execute normal code again. If not specified, this is assumed
+to be entry-latency + exit-latency.
+
+These timing parameters can be used by an OS in different circumstances.
+
+An idle CPU requires the expected min-residency time to select the most
+appropriate idle state based on the expected expiry time of the next IRQ
+(ie wake-up) that causes the CPU to return to the EXEC phase.
+
+An operating system scheduler may need to compute the shortest wake-up delay
+for CPUs in the system by detecting how long will it take to get a CPU out
+of an idle state, eg:
+
+wakeup-delay = exit-latency + max(entry-latency - (now - entry-timestamp), 0)
+
+In other words, the scheduler can make its scheduling decision by selecting
+(eg waking-up) the CPU with the shortest wake-up latency.
+The wake-up latency must take into account the entry latency if that period
+has not expired. The abortable nature of the PREP period can be ignored
+if it cannot be relied upon (e.g. the PREP deadline may occur much sooner than
+the worst case since it depends on the CPU operating conditions, ie caches
+state).
+
+An OS has to reliably probe the wakeup-latency since some devices can enforce
+latency constraints guarantees to work properly, so the OS has to detect the
+worst case wake-up latency it can incur if a CPU is allowed to enter an
+idle state, and possibly to prevent that to guarantee reliable device
+functioning.
+
+The min-residency time parameter deserves further explanation since it is
+expressed in time units but must factor in energy consumption coefficients.
+
+The energy consumption of a cpu when it enters a power state can be roughly
+characterised by the following graph:
+
+ |
+ |
+ |
+ e |
+ n | /---
+ e | /------
+ r | /------
+ g | /-----
+ y | /------
+ | ----
+ | /|
+ | / |
+ | / |
+ | / |
+ | / |
+ | / |
+ |/ |
+ -----|-------+----------------------------------
+ 0| 1 time(ms)
+
+ Graph 1: Energy vs time example
+
+The graph is split in two parts delimited by time 1ms on the X-axis.
+The graph curve with X-axis values = { x | 0 < x < 1ms } has a steep slope
+and denotes the energy costs incurred whilst entering and leaving the idle
+state.
+The graph curve in the area delimited by X-axis values = {x | x > 1ms } has
+shallower slope and essentially represents the energy consumption of the idle
+state.
+
+min-residency is defined for a given idle state as the minimum expected
+residency time for a state (inclusive of preparation and entry) after
+which choosing that state become the most energy efficient option. A good
+way to visualise this, is by taking the same graph above and comparing some
+states energy consumptions plots.
+
+For sake of simplicity, let's consider a system with two idle states IDLE1,
+and IDLE2:
+
+ |
+ |
+ |
+ | /-- IDLE1
+ e | /---
+ n | /----
+ e | /---
+ r | /-----/--------- IDLE2
+ g | /-------/---------
+ y | ------------ /---|
+ | / /---- |
+ | / /--- |
+ | / /---- |
+ | / /--- |
+ | --- |
+ | / |
+ | / |
+ |/ | time
+ ---/----------------------------+------------------------
+ |IDLE1-energy < IDLE2-energy | IDLE2-energy < IDLE1-energy
+ |
+ IDLE2-min-residency
+
+ Graph 2: idle states min-residency example
+
+In graph 2 above, that takes into account idle states entry/exit energy
+costs, it is clear that if the idle state residency time (ie time till next
+wake-up IRQ) is less than IDLE2-min-residency, IDLE1 is the better idle state
+choice energywise.
+
+This is mainly down to the fact that IDLE1 entry/exit energy costs are lower
+than IDLE2.
+
+However, the lower power consumption (ie shallower energy curve slope) of idle
+state IDLE2 implies that after a suitable time, IDLE2 becomes more energy
+efficient.
+
+The time@which IDLE2 becomes more energy efficient than IDLE1 (and other
+shallower states in a system with multiple idle states) is defined
+IDLE2-min-residency and corresponds to the time when energy consumption of
+IDLE1 and IDLE2 states breaks even.
+
+The definitions provided in this section underpin the idle states
+properties specification that is the subject of the following sections.
+
+===========================================
+3 - idle-states node
+===========================================
+
+ARM processor idle states are defined within the idle-states node, which is
+a direct child of the cpus node [1] and provides a container where the
+processor idle states, defined as device tree nodes, are listed.
+
+- idle-states node
+
+ Usage: Optional - On ARM systems, it is a container of processor idle
+ states nodes. If the system does not provide CPU
+ power management capabilities or the processor just
+ supports idle_standby an idle-states node is not
+ required.
+
+ Description: idle-states node is a container node, where its
+ subnodes describe the CPU idle states.
+
+ Node name must be "idle-states".
+
+ The idle-states node's parent node must be the cpus node.
+
+ The idle-states node's child nodes can be:
+
+ - one or more state nodes
+
+ Any other configuration is considered invalid.
+
+ An idle-states node defines the following properties:
+
+ - entry-method
+ Value type: <stringlist>
+ Usage and definition depend on ARM architecture version.
+ # On ARM v8 64-bit this property is required and must
+ be one of:
+ - "psci" (see bindings in [2])
+ # On ARM 32-bit systems this property is optional
+
+The nodes describing the idle states (state) can only be defined within the
+idle-states node, any other configuration is considered invalid and therefore
+must be ignored.
+
+===========================================
+4 - state node
+===========================================
+
+A state node represents an idle state description and must be defined as
+follows:
+
+- state node
+
+ Description: must be child of the idle-states node
+
+ The state node name shall follow standard device tree naming
+ rules ([5], 2.2.1 "Node names"), in particular state nodes which
+ are siblings within a single common parent must be given a unique name.
+
+ The idle state entered by executing the wfi instruction (idle_standby
+ SBSA,[3][4]) is considered standard on all ARM platforms and therefore
+ must not be listed.
+
+ With the definitions provided above, the following list represents
+ the valid properties for a state node:
+
+ - compatible
+ Usage: Required
+ Value type: <stringlist>
+ Definition: Must be "arm,idle-state".
+
+ - local-timer-stop
+ Usage: See definition
+ Value type: <none>
+ Definition: if present the CPU local timer control logic is
+ lost on state entry, otherwise it is retained.
+
+ - entry-latency-us
+ Usage: Required
+ Value type: <prop-encoded-array>
+ Definition: u32 value representing worst case latency in
+ microseconds required to enter the idle state.
+ The exit-latency-us duration may be guaranteed
+ only after entry-latency-us has passed.
+
+ - exit-latency-us
+ Usage: Required
+ Value type: <prop-encoded-array>
+ Definition: u32 value representing worst case latency
+ in microseconds required to exit the idle state.
+
+ - min-residency-us
+ Usage: Required
+ Value type: <prop-encoded-array>
+ Definition: u32 value representing minimum residency duration
+ in microseconds, inclusive of preparation and
+ entry, for this idle state to be considered
+ worthwhile energy wise (refer to section 2 of
+ this document for a complete description).
+
+ - wakeup-latency-us:
+ Usage: Optional
+ Value type: <prop-encoded-array>
+ Definition: u32 value representing maximum delay between the
+ signaling of a wake-up event and the CPU being
+ able to execute normal code again. If omitted,
+ this is assumed to be equal to:
+
+ entry-latency-us + exit-latency-us
+
+ It is important to supply this value on systems
+ where the duration of PREP phase (see diagram 1,
+ section 2) is non-neglibigle.
+ In such systems entry-latency-us + exit-latency-us
+ will exceed wakeup-latency-us by this duration.
+
+ In addition to the properties listed above, a state node may require
+ additional properties specifics to the entry-method defined in the
+ idle-states node, please refer to the entry-method bindings
+ documentation for properties definitions.
+
+===========================================
+4 - Examples
+===========================================
+
+Example 1 (ARM 64-bit, 16-cpu system, PSCI enable-method):
+
+cpus {
+ #size-cells = <0>;
+ #address-cells = <2>;
+
+ CPU0: cpu at 0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU1: cpu at 1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU2: cpu at 100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x100>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU3: cpu at 101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x101>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU4: cpu at 10000 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x10000>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU5: cpu at 10001 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x10001>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU6: cpu at 10100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x10100>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU7: cpu at 10101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x10101>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU8: cpu at 100000000 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x0>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU9: cpu at 100000001 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x1>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU10: cpu at 100000100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x100>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU11: cpu at 100000101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x101>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU12: cpu at 100010000 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x10000>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU13: cpu at 100010001 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x10001>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU14: cpu at 100010100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x10100>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU15: cpu at 100010101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x10101>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ idle-states {
+ entry-method = "arm,psci";
+
+ CPU_RETENTION_0_0: cpu-retention-0-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x0010000>;
+ entry-latency-us = <20>;
+ exit-latency-us = <40>;
+ min-residency-us = <80>;
+ };
+
+ CLUSTER_RETENTION_0: cluster-retention-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x1010000>;
+ entry-latency-us = <50>;
+ exit-latency-us = <100>;
+ min-residency-us = <250>;
+ wakeup-latency-us = <130>;
+ };
+
+ CPU_SLEEP_0_0: cpu-sleep-0-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x0010000>;
+ entry-latency-us = <250>;
+ exit-latency-us = <500>;
+ min-residency-us = <950>;
+ };
+
+ CLUSTER_SLEEP_0: cluster-sleep-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x1010000>;
+ entry-latency-us = <600>;
+ exit-latency-us = <1100>;
+ min-residency-us = <2700>;
+ wakeup-latency-us = <1500>;
+ };
+
+ CPU_RETENTION_1_0: cpu-retention-1-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x0010000>;
+ entry-latency-us = <20>;
+ exit-latency-us = <40>;
+ min-residency-us = <90>;
+ };
+
+ CLUSTER_RETENTION_1: cluster-retention-1 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x1010000>;
+ entry-latency-us = <50>;
+ exit-latency-us = <100>;
+ min-residency-us = <270>;
+ wakeup-latency-us = <100>;
+ };
+
+ CPU_SLEEP_1_0: cpu-sleep-1-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x0010000>;
+ entry-latency-us = <70>;
+ exit-latency-us = <100>;
+ min-residency-us = <300>;
+ wakeup-latency-us = <150>;
+ };
+
+ CLUSTER_SLEEP_1: cluster-sleep-1 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x1010000>;
+ entry-latency-us = <500>;
+ exit-latency-us = <1200>;
+ min-residency-us = <3500>;
+ wakeup-latency-us = <1300>;
+ };
+ };
+
+};
+
+Example 2 (ARM 32-bit, 8-cpu system, two clusters):
+
+cpus {
+ #size-cells = <0>;
+ #address-cells = <1>;
+
+ CPU0: cpu at 0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x0>;
+ cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU1: cpu at 1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x1>;
+ cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU2: cpu at 2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x2>;
+ cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU3: cpu at 3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x3>;
+ cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU4: cpu at 100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x100>;
+ cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU5: cpu at 101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x101>;
+ cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU6: cpu at 102 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x102>;
+ cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU7: cpu at 103 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x103>;
+ cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+ };
+
+ idle-states {
+ CPU_SLEEP_0_0: cpu-sleep-0-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ entry-latency-us = <200>;
+ exit-latency-us = <100>;
+ min-residency-us = <400>;
+ wakeup-latency-us = <250>;
+ };
+
+ CLUSTER_SLEEP_0: cluster-sleep-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ entry-latency-us = <500>;
+ exit-latency-us = <1500>;
+ min-residency-us = <2500>;
+ wakeup-latency-us = <1700>;
+ };
+
+ CPU_SLEEP_1_0: cpu-sleep-1-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ entry-latency-us = <300>;
+ exit-latency-us = <500>;
+ min-residency-us = <900>;
+ wakeup-latency-us = <600>;
+ };
+
+ CLUSTER_SLEEP_1: cluster-sleep-1 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ entry-latency-us = <800>;
+ exit-latency-us = <2000>;
+ min-residency-us = <6500>;
+ wakeup-latency-us = <2300>;
+ };
+ };
+
+};
+
+===========================================
+5 - References
+===========================================
+
+[1] ARM Linux Kernel documentation - CPUs bindings
+ Documentation/devicetree/bindings/arm/cpus.txt
+
+[2] ARM Linux Kernel documentation - PSCI bindings
+ Documentation/devicetree/bindings/arm/psci.txt
+
+[3] ARM Server Base System Architecture (SBSA)
+ http://infocenter.arm.com/help/index.jsp
+
+[4] ARM Architecture Reference Manuals
+ http://infocenter.arm.com/help/index.jsp
+
+[5] ePAPR standard
+ https://www.power.org/documentation/epapr-version-1-1/
diff --git a/Documentation/devicetree/bindings/arm/psci.txt b/Documentation/devicetree/bindings/arm/psci.txt
index b4a58f3..5aa40ed 100644
--- a/Documentation/devicetree/bindings/arm/psci.txt
+++ b/Documentation/devicetree/bindings/arm/psci.txt
@@ -50,6 +50,16 @@ Main node optional properties:
- migrate : Function ID for MIGRATE operation
+Device tree nodes that require usage of PSCI CPU_SUSPEND function (ie idle
+state nodes, as per bindings in [1]) must specify the following properties:
+
+- arm,psci-suspend-param
+ Usage: Required for state nodes[1] if the corresponding
+ idle-states node entry-method property is set
+ to "psci".
+ Value type: <u32>
+ Definition: power_state parameter to pass to the PSCI
+ suspend call.
Example:
@@ -64,7 +74,6 @@ Case 1: PSCI v0.1 only.
migrate = <0x95c10003>;
};
-
Case 2: PSCI v0.2 only
psci {
@@ -88,3 +97,6 @@ Case 3: PSCI v0.2 and PSCI v0.1.
...
};
+
+[1] Kernel documentation - ARM idle states bindings
+ Documentation/devicetree/bindings/arm/idle-states.txt
--
1.9.1
^ permalink raw reply related
* [PATCH v6 2/7] drivers: cpuidle: implement DT based idle states infrastructure
From: Lorenzo Pieralisi @ 2014-07-21 16:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405958786-17243-1-git-send-email-lorenzo.pieralisi@arm.com>
On most common ARM systems, the low-power states a CPU can be put into are
not discoverable in HW and require device tree bindings to describe
power down suspend operations and idle states parameters.
In order to enable DT based idle states and configure idle drivers, this
patch implements the bulk infrastructure required to parse the device tree
idle states bindings and initialize the corresponding CPUidle driver states
data.
The parsing API accepts a start index that defines the first idle state
that should be initialized by the parsing code in order to give new and
legacy driver flexibility over which states should be parsed using the
new DT mechanism.
The idle states list is obtained from the first cpu in the driver
cpumask, which implicitly means the parsing code expects idle states
(and related list of phandles) to be the same for all CPUs in the
CPUidle driver mask. The kernel does not check this assumption, it must
be enforced by the bootloader to ensure correct system behaviour.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
drivers/cpuidle/Kconfig | 8 +++
drivers/cpuidle/Makefile | 1 +
drivers/cpuidle/dt_idle_states.c | 138 +++++++++++++++++++++++++++++++++++++++
drivers/cpuidle/dt_idle_states.h | 5 ++
4 files changed, 152 insertions(+)
create mode 100644 drivers/cpuidle/dt_idle_states.c
create mode 100644 drivers/cpuidle/dt_idle_states.h
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
index 1b96fb9..414e7a96 100644
--- a/drivers/cpuidle/Kconfig
+++ b/drivers/cpuidle/Kconfig
@@ -30,6 +30,14 @@ config CPU_IDLE_GOV_MENU
bool "Menu governor (for tickless system)"
default y
+config DT_IDLE_STATES
+ bool "Idle states DT support"
+ depends on ARM || ARM64
+ help
+ Allows the CPU idle framework to initialize CPU idle drivers
+ state data by using DT provided nodes compliant with idle states
+ device tree bindings.
+
menu "ARM CPU Idle Drivers"
depends on ARM
source "drivers/cpuidle/Kconfig.arm"
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index d8bb1ff..b27a062 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -4,6 +4,7 @@
obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
+obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o
##################################################################################
# ARM SoC drivers
diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c
new file mode 100644
index 0000000..5413132
--- /dev/null
+++ b/drivers/cpuidle/dt_idle_states.c
@@ -0,0 +1,138 @@
+/*
+ * DT idle states parsing code.
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "DT idle-states: " fmt
+
+#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "dt_idle_states.h"
+
+static int init_state_node(struct cpuidle_state *idle_state,
+ struct device_node *state_node)
+{
+ int err;
+
+ err = of_property_read_u32(state_node, "wakeup-latency-us",
+ &idle_state->exit_latency);
+ if (err) {
+ u32 entry_latency, exit_latency;
+
+ err = of_property_read_u32(state_node, "entry-latency-us",
+ &entry_latency);
+ if (err) {
+ pr_debug(" * %s missing entry-latency-us property\n",
+ state_node->full_name);
+ return -EINVAL;
+ }
+
+ err = of_property_read_u32(state_node, "exit-latency-us",
+ &exit_latency);
+ if (err) {
+ pr_debug(" * %s missing exit-latency-us property\n",
+ state_node->full_name);
+ return -EINVAL;
+ }
+ /*
+ * If wakeup-latency-us is missing, default to entry+exit
+ * latencies as defined in idle states bindings
+ */
+ idle_state->exit_latency = entry_latency + exit_latency;
+ }
+
+ err = of_property_read_u32(state_node, "min-residency-us",
+ &idle_state->target_residency);
+ if (err) {
+ pr_debug(" * %s missing min-residency-us property\n",
+ state_node->full_name);
+ return -EINVAL;
+ }
+
+ idle_state->flags = CPUIDLE_FLAG_TIME_VALID;
+ if (of_property_read_bool(state_node, "local-timer-stop"))
+ idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP;
+ /*
+ * TODO:
+ * replace with kstrdup and pointer assignment when name
+ * and desc become string pointers
+ */
+ strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1);
+ strncpy(idle_state->desc, state_node->name, CPUIDLE_DESC_LEN - 1);
+ return 0;
+}
+
+/**
+ * dt_init_idle_driver() - Parse the DT idle states and initialize the
+ * idle driver states array
+ *
+ * @drv: Pointer to CPU idle driver to be initialized
+ * @start_idx: First idle state index to be initialized
+ *
+ * On success the states array in the cpuidle driver contains
+ * initialized entries in the states array, starting from index start_idx.
+ *
+ * Return:
+ * 0 on success
+ * <0 on failure
+ */
+int dt_init_idle_driver(struct cpuidle_driver *drv, unsigned int start_idx)
+{
+ unsigned int i, state_idx = start_idx;
+ struct cpuidle_state *idle_state;
+ struct device_node *state_node, *cpu_node;
+
+
+ if (state_idx >= CPUIDLE_STATE_MAX)
+ return -EINVAL;
+ /*
+ * We get the idle states for the first logical cpu in the
+ * driver mask. The kernel does not check idle states on all
+ * cpus in the driver mask, they are assumed to be the same
+ * by default.
+ */
+ cpu_node = of_cpu_device_node_get(cpumask_first(drv->cpumask));
+
+ for (i = 0; ; i++) {
+ int err;
+
+ state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+ if (!state_node)
+ break;
+
+ if (state_idx == CPUIDLE_STATE_MAX) {
+ pr_warn("State index reached static CPU idle driver states array size\n");
+ break;
+ }
+
+ idle_state = &drv->states[state_idx++];
+ err = init_state_node(idle_state, state_node);
+ if (err)
+ return err;
+ }
+
+ /*
+ * If no idle states are detected, return an error and let the idle
+ * driver initialization fail accordingly since initializing a driver
+ * with simple WFI as an idle state is equivalent to letting the
+ * kernel run the default idle loop.
+ */
+ if (!i)
+ return -ENODATA;
+
+ drv->state_count = state_idx;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dt_init_idle_driver);
diff --git a/drivers/cpuidle/dt_idle_states.h b/drivers/cpuidle/dt_idle_states.h
new file mode 100644
index 0000000..728c37c
--- /dev/null
+++ b/drivers/cpuidle/dt_idle_states.h
@@ -0,0 +1,5 @@
+#ifndef __DT_IDLE_STATES
+#define __DT_IDLE_STATES
+
+int dt_init_idle_driver(struct cpuidle_driver *drv, unsigned int start_idx);
+#endif
--
1.9.1
^ permalink raw reply related
* [PATCH v6 3/7] arm64: kernel: introduce cpu_init_idle CPU operation
From: Lorenzo Pieralisi @ 2014-07-21 16:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405958786-17243-1-git-send-email-lorenzo.pieralisi@arm.com>
The CPUidle subsystem on ARM64 machines requires the idle states
implementation back-end to initialize idle states parameter upon
boot. This patch adds a hook in the CPU operations structure that
should be initialized by the CPU operations back-end in order to
provide a function that initializes cpu idle states.
This patch also adds the infrastructure to arm64 kernel required
to export the CPU operations based initialization interface, so
that drivers (ie CPUidle) can use it when they are initialized
at probe time.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/include/asm/cpu_ops.h | 1 +
arch/arm64/include/asm/cpuidle.h | 13 +++++++++++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/cpuidle.c | 27 +++++++++++++++++++++++++++
4 files changed, 42 insertions(+)
create mode 100644 arch/arm64/include/asm/cpuidle.h
create mode 100644 arch/arm64/kernel/cpuidle.c
diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h
index d7b4b38..c156498 100644
--- a/arch/arm64/include/asm/cpu_ops.h
+++ b/arch/arm64/include/asm/cpu_ops.h
@@ -47,6 +47,7 @@ struct device_node;
struct cpu_operations {
const char *name;
int (*cpu_init)(struct device_node *, unsigned int);
+ int (*cpu_init_idle)(struct device_node *, unsigned int);
int (*cpu_prepare)(unsigned int);
int (*cpu_boot)(unsigned int);
void (*cpu_postboot)(void);
diff --git a/arch/arm64/include/asm/cpuidle.h b/arch/arm64/include/asm/cpuidle.h
new file mode 100644
index 0000000..b52a993
--- /dev/null
+++ b/arch/arm64/include/asm/cpuidle.h
@@ -0,0 +1,13 @@
+#ifndef __ASM_CPUIDLE_H
+#define __ASM_CPUIDLE_H
+
+#ifdef CONFIG_CPU_IDLE
+extern int cpu_init_idle(unsigned int cpu);
+#else
+static inline int cpu_init_idle(unsigned int cpu)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+#endif
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index cdaedad..7b0c068 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -26,6 +26,7 @@ arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
+arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
arm64-obj-$(CONFIG_KGDB) += kgdb.o
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
diff --git a/arch/arm64/kernel/cpuidle.c b/arch/arm64/kernel/cpuidle.c
new file mode 100644
index 0000000..46eb3ea
--- /dev/null
+++ b/arch/arm64/kernel/cpuidle.c
@@ -0,0 +1,27 @@
+/*
+ * ARM64 CPU idle arch support
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+ *
+ * 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.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <asm/cpu_ops.h>
+
+int cpu_init_idle(unsigned int cpu)
+{
+ struct device_node *cpu_node = of_cpu_device_node_get(cpu);
+
+ if (!cpu_node)
+ return -ENODEV;
+
+ if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle)
+ return cpu_ops[cpu]->cpu_init_idle(cpu_node, cpu);
+
+ return -EOPNOTSUPP;
+}
--
1.9.1
^ permalink raw reply related
* [PATCH v6 4/7] arm64: add PSCI CPU_SUSPEND based cpu_suspend support
From: Lorenzo Pieralisi @ 2014-07-21 16:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1405958786-17243-1-git-send-email-lorenzo.pieralisi@arm.com>
This patch implements the cpu_suspend cpu operations method through
the PSCI CPU SUSPEND API. The PSCI implementation translates the idle state
index passed by the cpu_suspend core call into a valid PSCI state according to
the PSCI states initialized at boot through the cpu_init_idle() CPU
operations hook.
Entry point is set to cpu_resume physical address, that represents the
default kernel execution address following a CPU reset; for standby
states the entry point address is useless and will therefore be ignored
by the PSCI implementation.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/kernel/psci.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
index a623c44..bbdf41d 100644
--- a/arch/arm64/kernel/psci.c
+++ b/arch/arm64/kernel/psci.c
@@ -21,6 +21,7 @@
#include <linux/reboot.h>
#include <linux/pm.h>
#include <linux/delay.h>
+#include <linux/slab.h>
#include <uapi/linux/psci.h>
#include <asm/compiler.h>
@@ -28,6 +29,7 @@
#include <asm/errno.h>
#include <asm/psci.h>
#include <asm/smp_plat.h>
+#include <asm/suspend.h>
#include <asm/system_misc.h>
#define PSCI_POWER_STATE_TYPE_STANDBY 0
@@ -65,6 +67,8 @@ enum psci_function {
PSCI_FN_MAX,
};
+static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state);
+
static u32 psci_function_id[PSCI_FN_MAX];
static int psci_to_linux_errno(int errno)
@@ -93,6 +97,18 @@ static u32 psci_power_state_pack(struct psci_power_state state)
& PSCI_0_2_POWER_STATE_AFFL_MASK);
}
+static void psci_power_state_unpack(u32 power_state,
+ struct psci_power_state *state)
+{
+ state->id = (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >>
+ PSCI_0_2_POWER_STATE_ID_SHIFT;
+ state->type = (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >>
+ PSCI_0_2_POWER_STATE_TYPE_SHIFT;
+ state->affinity_level =
+ (power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >>
+ PSCI_0_2_POWER_STATE_AFFL_SHIFT;
+}
+
/*
* The following two functions are invoked via the invoke_psci_fn pointer
* and will not be inlined, allowing us to piggyback on the AAPCS.
@@ -199,6 +215,61 @@ static int psci_migrate_info_type(void)
return err;
}
+static int cpu_psci_cpu_init_idle(struct device_node *cpu_node,
+ unsigned int cpu)
+{
+ int i, ret, count = 0;
+ struct psci_power_state *psci_states;
+ struct device_node *state_node;
+
+ /*
+ * If the PSCI cpu_suspend function hook has not been initialized
+ * idle states must not be enabled, so bail out
+ */
+ if (!psci_ops.cpu_suspend)
+ return -EOPNOTSUPP;
+
+ /* Count idle states */
+ while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
+ count)))
+ count++;
+
+ if (!count)
+ return -ENODEV;
+
+ psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
+ if (!psci_states) {
+ pr_warn("idle state psci_state allocation failed\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < count; i++) {
+ u32 psci_power_state;
+
+ state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+
+ ret = of_property_read_u32(state_node,
+ "arm,psci-suspend-param",
+ &psci_power_state);
+ if (ret) {
+ pr_warn(" * %s missing arm,psci-suspend-param property\n",
+ state_node->full_name);
+ goto free_mem;
+ }
+
+ pr_debug("psci-power-state %#x index %d\n", psci_power_state,
+ i);
+ psci_power_state_unpack(psci_power_state, &psci_states[i]);
+ }
+ /* Idle states parsed correctly, initialize per-cpu pointer */
+ per_cpu(psci_power_state, cpu) = psci_states;
+ return 0;
+
+free_mem:
+ kfree(psci_states);
+ return ret;
+}
+
static int get_set_conduit_method(struct device_node *np)
{
const char *method;
@@ -436,8 +507,23 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
#endif
#endif
+static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
+{
+ struct psci_power_state *state = __get_cpu_var(psci_power_state);
+ /*
+ * idle state index 0 corresponds to wfi, should never be called
+ * from the cpu_suspend operations
+ */
+ if (WARN_ON_ONCE(!index))
+ return -EINVAL;
+
+ return psci_ops.cpu_suspend(state[index - 1],
+ virt_to_phys(cpu_resume));
+}
+
const struct cpu_operations cpu_psci_ops = {
.name = "psci",
+ .cpu_init_idle = cpu_psci_cpu_init_idle,
#ifdef CONFIG_SMP
.cpu_init = cpu_psci_cpu_init,
.cpu_prepare = cpu_psci_cpu_prepare,
@@ -448,5 +534,8 @@ const struct cpu_operations cpu_psci_ops = {
.cpu_kill = cpu_psci_cpu_kill,
#endif
#endif
+#ifdef CONFIG_ARM64_CPU_SUSPEND
+ .cpu_suspend = cpu_psci_cpu_suspend,
+#endif
};
--
1.9.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox