diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c index c8d7d45a5a97..be667e1aac93 100644 --- a/drivers/clk/tegra/clk-dfll.c +++ b/drivers/clk/tegra/clk-dfll.c @@ -359,6 +359,9 @@ struct tegra_dfll { struct pinctrl_state *pwm_enable_state; struct pinctrl_state *pwm_disable_state; u32 reg_init_uV; + + /* spinlock protecting register accesses */ + spinlock_t lock; }; #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw) @@ -1106,6 +1109,7 @@ static int dfll_request_rate(struct tegra_dfll *td, unsigned long rate) { int ret; struct dfll_rate_req req; + unsigned long flags; if (td->mode == DFLL_UNINITIALIZED) { dev_err(td->dev, "%s: Cannot set DFLL rate in %s mode\n", @@ -1121,8 +1125,10 @@ static int dfll_request_rate(struct tegra_dfll *td, unsigned long rate) td->last_req = req; if (td->mode == DFLL_CLOSED_LOOP) { + spin_lock_irqsave(&td->lock, flags); dfll_set_close_loop_config(td, &td->last_req); dfll_set_frequency_request(td, &td->last_req); + spin_unlock_irqrestore(&td->lock, flags); } return 0; @@ -1141,13 +1147,18 @@ static int dfll_request_rate(struct tegra_dfll *td, unsigned long rate) */ static int dfll_disable(struct tegra_dfll *td) { + unsigned long flags; + if (td->mode != DFLL_OPEN_LOOP) { dev_err(td->dev, "cannot disable DFLL in %s mode\n", mode_name[td->mode]); return -EINVAL; } + spin_lock_irqsave(&td->lock, flags); dfll_set_mode(td, DFLL_DISABLED); + spin_unlock_irqrestore(&td->lock, flags); + pm_runtime_put_sync(td->dev); return 0; @@ -1211,6 +1222,7 @@ static void dfll_set_open_loop_config(struct tegra_dfll *td) static int dfll_lock(struct tegra_dfll *td) { struct dfll_rate_req *req = &td->last_req; + unsigned long flags; switch (td->mode) { case DFLL_CLOSED_LOOP: @@ -1228,10 +1240,12 @@ static int dfll_lock(struct tegra_dfll *td) else dfll_i2c_set_output_enabled(td, true); + spin_lock_irqsave(&td->lock, flags); dfll_set_mode(td, DFLL_CLOSED_LOOP); dfll_set_close_loop_config(td, req); dfll_set_frequency_request(td, req); dfll_set_force_output_enabled(td, false); + spin_unlock_irqrestore(&td->lock, flags); return 0; default: @@ -1251,14 +1265,19 @@ static int dfll_lock(struct tegra_dfll *td) */ static int dfll_unlock(struct tegra_dfll *td) { + unsigned long flags; + switch (td->mode) { case DFLL_CLOSED_LOOP: + spin_lock_irqsave(&td->lock, flags); dfll_set_open_loop_config(td); dfll_set_mode(td, DFLL_OPEN_LOOP); if (td->pmu_if == TEGRA_DFLL_PMU_PWM) dfll_pwm_set_output_enabled(td, false); else dfll_i2c_set_output_enabled(td, false); + + spin_unlock_irqrestore(&td->lock, flags); return 0; case DFLL_OPEN_LOOP: @@ -1521,8 +1540,10 @@ static int attr_registers_show(struct seq_file *s, void *data) { u32 val, offs; struct tegra_dfll *td = s->private; + unsigned long flags; seq_puts(s, "CONTROL REGISTERS:\n"); + spin_lock_irqsave(&td->lock, flags); for (offs = 0; offs <= DFLL_MONITOR_DATA; offs += 4) { if (offs == DFLL_OUTPUT_CFG) val = dfll_i2c_readl(td, offs); @@ -1551,6 +1572,8 @@ static int attr_registers_show(struct seq_file *s, void *data) __raw_readl(td->lut_base + offs)); } + spin_unlock_irqrestore(&td->lock, flags); + return 0; } @@ -2292,6 +2315,8 @@ int tegra_dfll_register(struct platform_device *pdev, if (ret) return ret; + spin_lock_init(&td->lock); + ret = dfll_register_clk(td); if (ret) { dev_err(&pdev->dev, "DFLL clk registration failed\n");