diff options
author | Wenyou Yang <wenyou.yang@microchip.com> | 2017-10-10 05:46:37 +0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@osg.samsung.com> | 2017-10-27 14:56:05 +0300 |
commit | 64f6306a5af21561ad6b9a3247fc68126b8be88a (patch) | |
tree | 5446eeb015fd6ac2cbbcba362c75cf5b4f71d74d /drivers/media | |
parent | cab1dea3be2e51578032c5aee6ac13ff7b4aaeb4 (diff) | |
download | linux-64f6306a5af21561ad6b9a3247fc68126b8be88a.tar.xz |
media: atmel-isc: Add prepare and unprepare ops
A software write operation to the ISC_CLKEN or ISC_CLKDIS register
requires double clock domain synchronization and is not permitted
when the ISC_SR.SIP is asserted. So add the .prepare and .unprepare
ops to make sure the ISC_CLKSR.SIP is unasserted before the write
operation to the ISC_CLKEN or ISC_CLKDIS register.
Signed-off-by: Wenyou Yang <wenyou.yang@microchip.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/platform/atmel/atmel-isc-regs.h | 1 | ||||
-rw-r--r-- | drivers/media/platform/atmel/atmel-isc.c | 40 |
2 files changed, 41 insertions, 0 deletions
diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h index 6936ac467609..93e58fcf1d5f 100644 --- a/drivers/media/platform/atmel/atmel-isc-regs.h +++ b/drivers/media/platform/atmel/atmel-isc-regs.h @@ -42,6 +42,7 @@ /* ISC Clock Status Register */ #define ISC_CLKSR 0x00000020 +#define ISC_CLKSR_SIP BIT(31) #define ISC_CLK(n) BIT(n) diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c index 991f962b7023..5f8228fc9c8d 100644 --- a/drivers/media/platform/atmel/atmel-isc.c +++ b/drivers/media/platform/atmel/atmel-isc.c @@ -308,6 +308,44 @@ module_param(sensor_preferred, uint, 0644); MODULE_PARM_DESC(sensor_preferred, "Sensor is preferred to output the specified format (1-on 0-off), default 1"); +static int isc_wait_clk_stable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + struct regmap *regmap = isc_clk->regmap; + unsigned long timeout = jiffies + usecs_to_jiffies(1000); + unsigned int status; + + while (time_before(jiffies, timeout)) { + regmap_read(regmap, ISC_CLKSR, &status); + if (!(status & ISC_CLKSR_SIP)) + return 0; + + usleep_range(10, 250); + } + + return -ETIMEDOUT; +} + +static int isc_clk_prepare(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + if (isc_clk->id == ISC_MCK) + pm_runtime_get_sync(isc_clk->dev); + + return isc_wait_clk_stable(hw); +} + +static void isc_clk_unprepare(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + isc_wait_clk_stable(hw); + + if (isc_clk->id == ISC_MCK) + pm_runtime_put_sync(isc_clk->dev); +} + static int isc_clk_enable(struct clk_hw *hw) { struct isc_clk *isc_clk = to_isc_clk(hw); @@ -459,6 +497,8 @@ static int isc_clk_set_rate(struct clk_hw *hw, } static const struct clk_ops isc_clk_ops = { + .prepare = isc_clk_prepare, + .unprepare = isc_clk_unprepare, .enable = isc_clk_enable, .disable = isc_clk_disable, .is_enabled = isc_clk_is_enabled, |