|
@@ -36,6 +36,7 @@ struct nv04_timer_priv {
|
|
|
struct nouveau_timer base;
|
|
struct nouveau_timer base;
|
|
|
struct list_head alarms;
|
|
struct list_head alarms;
|
|
|
spinlock_t lock;
|
|
spinlock_t lock;
|
|
|
|
|
+ u64 suspend_time;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
static u64
|
|
static u64
|
|
@@ -112,6 +113,25 @@ nv04_timer_alarm(struct nouveau_timer *ptimer, u64 time,
|
|
|
nv04_timer_alarm_trigger(ptimer);
|
|
nv04_timer_alarm_trigger(ptimer);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static void
|
|
|
|
|
+nv04_timer_alarm_cancel(struct nouveau_timer *ptimer,
|
|
|
|
|
+ struct nouveau_alarm *alarm)
|
|
|
|
|
+{
|
|
|
|
|
+ struct nv04_timer_priv *priv = (void *)ptimer;
|
|
|
|
|
+ unsigned long flags;
|
|
|
|
|
+
|
|
|
|
|
+ /* avoid deleting an entry while the alarm intr is running */
|
|
|
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
|
|
|
+
|
|
|
|
|
+ /* delete the alarm from the list */
|
|
|
|
|
+ list_del(&alarm->head);
|
|
|
|
|
+
|
|
|
|
|
+ /* reset the head so as list_empty returns 1 */
|
|
|
|
|
+ INIT_LIST_HEAD(&alarm->head);
|
|
|
|
|
+
|
|
|
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static void
|
|
static void
|
|
|
nv04_timer_intr(struct nouveau_subdev *subdev)
|
|
nv04_timer_intr(struct nouveau_subdev *subdev)
|
|
|
{
|
|
{
|
|
@@ -146,6 +166,8 @@ nv04_timer_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
|
|
priv->base.base.intr = nv04_timer_intr;
|
|
priv->base.base.intr = nv04_timer_intr;
|
|
|
priv->base.read = nv04_timer_read;
|
|
priv->base.read = nv04_timer_read;
|
|
|
priv->base.alarm = nv04_timer_alarm;
|
|
priv->base.alarm = nv04_timer_alarm;
|
|
|
|
|
+ priv->base.alarm_cancel = nv04_timer_alarm_cancel;
|
|
|
|
|
+ priv->suspend_time = 0;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&priv->alarms);
|
|
INIT_LIST_HEAD(&priv->alarms);
|
|
|
spin_lock_init(&priv->lock);
|
|
spin_lock_init(&priv->lock);
|
|
@@ -164,7 +186,7 @@ nv04_timer_init(struct nouveau_object *object)
|
|
|
{
|
|
{
|
|
|
struct nouveau_device *device = nv_device(object);
|
|
struct nouveau_device *device = nv_device(object);
|
|
|
struct nv04_timer_priv *priv = (void *)object;
|
|
struct nv04_timer_priv *priv = (void *)object;
|
|
|
- u32 m = 1, f, n, d;
|
|
|
|
|
|
|
+ u32 m = 1, f, n, d, lo, hi;
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
ret = nouveau_timer_init(&priv->base);
|
|
ret = nouveau_timer_init(&priv->base);
|
|
@@ -221,16 +243,25 @@ nv04_timer_init(struct nouveau_object *object)
|
|
|
d >>= 1;
|
|
d >>= 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* restore the time before suspend */
|
|
|
|
|
+ lo = priv->suspend_time;
|
|
|
|
|
+ hi = (priv->suspend_time >> 32);
|
|
|
|
|
+
|
|
|
nv_debug(priv, "input frequency : %dHz\n", f);
|
|
nv_debug(priv, "input frequency : %dHz\n", f);
|
|
|
nv_debug(priv, "input multiplier: %d\n", m);
|
|
nv_debug(priv, "input multiplier: %d\n", m);
|
|
|
nv_debug(priv, "numerator : 0x%08x\n", n);
|
|
nv_debug(priv, "numerator : 0x%08x\n", n);
|
|
|
nv_debug(priv, "denominator : 0x%08x\n", d);
|
|
nv_debug(priv, "denominator : 0x%08x\n", d);
|
|
|
nv_debug(priv, "timer frequency : %dHz\n", (f * m) * d / n);
|
|
nv_debug(priv, "timer frequency : %dHz\n", (f * m) * d / n);
|
|
|
|
|
+ nv_debug(priv, "time low : 0x%08x\n", lo);
|
|
|
|
|
+ nv_debug(priv, "time high : 0x%08x\n", hi);
|
|
|
|
|
|
|
|
nv_wr32(priv, NV04_PTIMER_NUMERATOR, n);
|
|
nv_wr32(priv, NV04_PTIMER_NUMERATOR, n);
|
|
|
nv_wr32(priv, NV04_PTIMER_DENOMINATOR, d);
|
|
nv_wr32(priv, NV04_PTIMER_DENOMINATOR, d);
|
|
|
nv_wr32(priv, NV04_PTIMER_INTR_0, 0xffffffff);
|
|
nv_wr32(priv, NV04_PTIMER_INTR_0, 0xffffffff);
|
|
|
nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
|
|
nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
|
|
|
|
|
+ nv_wr32(priv, NV04_PTIMER_TIME_1, hi);
|
|
|
|
|
+ nv_wr32(priv, NV04_PTIMER_TIME_0, lo);
|
|
|
|
|
+
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -238,6 +269,8 @@ static int
|
|
|
nv04_timer_fini(struct nouveau_object *object, bool suspend)
|
|
nv04_timer_fini(struct nouveau_object *object, bool suspend)
|
|
|
{
|
|
{
|
|
|
struct nv04_timer_priv *priv = (void *)object;
|
|
struct nv04_timer_priv *priv = (void *)object;
|
|
|
|
|
+ if (suspend)
|
|
|
|
|
+ priv->suspend_time = nv04_timer_read(&priv->base);
|
|
|
nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
|
|
nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
|
|
|
return nouveau_timer_fini(&priv->base, suspend);
|
|
return nouveau_timer_fini(&priv->base, suspend);
|
|
|
}
|
|
}
|