return 0;
 }
 
+struct cmos_set_alarm_callback_param {
+       struct cmos_rtc *cmos;
+       unsigned char mon, mday, hrs, min, sec;
+       struct rtc_wkalrm *t;
+};
+
+/* Note: this function may be executed by mc146818_avoid_UIP() more then
+ *      once
+ */
+static void cmos_set_alarm_callback(unsigned char __always_unused seconds,
+                                   void *param_in)
+{
+       struct cmos_set_alarm_callback_param *p =
+               (struct cmos_set_alarm_callback_param *)param_in;
+
+       /* next rtc irq must not be from previous alarm setting */
+       cmos_irq_disable(p->cmos, RTC_AIE);
+
+       /* update alarm */
+       CMOS_WRITE(p->hrs, RTC_HOURS_ALARM);
+       CMOS_WRITE(p->min, RTC_MINUTES_ALARM);
+       CMOS_WRITE(p->sec, RTC_SECONDS_ALARM);
+
+       /* the system may support an "enhanced" alarm */
+       if (p->cmos->day_alrm) {
+               CMOS_WRITE(p->mday, p->cmos->day_alrm);
+               if (p->cmos->mon_alrm)
+                       CMOS_WRITE(p->mon, p->cmos->mon_alrm);
+       }
+
+       if (use_hpet_alarm()) {
+               /*
+                * FIXME the HPET alarm glue currently ignores day_alrm
+                * and mon_alrm ...
+                */
+               hpet_set_alarm_time(p->t->time.tm_hour, p->t->time.tm_min,
+                                   p->t->time.tm_sec);
+       }
+
+       if (p->t->enabled)
+               cmos_irq_enable(p->cmos, RTC_AIE);
+}
+
 static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
 {
        struct cmos_rtc *cmos = dev_get_drvdata(dev);
-       unsigned char mon, mday, hrs, min, sec, rtc_control;
+       struct cmos_set_alarm_callback_param p = {
+               .cmos = cmos,
+               .t = t
+       };
+       unsigned char rtc_control;
        int ret;
 
        /* This not only a rtc_op, but also called directly */
        if (ret < 0)
                return ret;
 
-       mon = t->time.tm_mon + 1;
-       mday = t->time.tm_mday;
-       hrs = t->time.tm_hour;
-       min = t->time.tm_min;
-       sec = t->time.tm_sec;
+       p.mon = t->time.tm_mon + 1;
+       p.mday = t->time.tm_mday;
+       p.hrs = t->time.tm_hour;
+       p.min = t->time.tm_min;
+       p.sec = t->time.tm_sec;
 
        spin_lock_irq(&rtc_lock);
        rtc_control = CMOS_READ(RTC_CONTROL);
 
        if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
                /* Writing 0xff means "don't care" or "match all".  */
-               mon = (mon <= 12) ? bin2bcd(mon) : 0xff;
-               mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff;
-               hrs = (hrs < 24) ? bin2bcd(hrs) : 0xff;
-               min = (min < 60) ? bin2bcd(min) : 0xff;
-               sec = (sec < 60) ? bin2bcd(sec) : 0xff;
-       }
-
-       spin_lock_irq(&rtc_lock);
-
-       /* next rtc irq must not be from previous alarm setting */
-       cmos_irq_disable(cmos, RTC_AIE);
-
-       /* update alarm */
-       CMOS_WRITE(hrs, RTC_HOURS_ALARM);
-       CMOS_WRITE(min, RTC_MINUTES_ALARM);
-       CMOS_WRITE(sec, RTC_SECONDS_ALARM);
-
-       /* the system may support an "enhanced" alarm */
-       if (cmos->day_alrm) {
-               CMOS_WRITE(mday, cmos->day_alrm);
-               if (cmos->mon_alrm)
-                       CMOS_WRITE(mon, cmos->mon_alrm);
-       }
-
-       if (use_hpet_alarm()) {
-               /*
-                * FIXME the HPET alarm glue currently ignores day_alrm
-                * and mon_alrm ...
-                */
-               hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min,
-                                   t->time.tm_sec);
+               p.mon = (p.mon <= 12) ? bin2bcd(p.mon) : 0xff;
+               p.mday = (p.mday >= 1 && p.mday <= 31) ? bin2bcd(p.mday) : 0xff;
+               p.hrs = (p.hrs < 24) ? bin2bcd(p.hrs) : 0xff;
+               p.min = (p.min < 60) ? bin2bcd(p.min) : 0xff;
+               p.sec = (p.sec < 60) ? bin2bcd(p.sec) : 0xff;
        }
 
-       if (t->enabled)
-               cmos_irq_enable(cmos, RTC_AIE);
-
-       spin_unlock_irq(&rtc_lock);
+       /*
+        * Some Intel chipsets disconnect the alarm registers when the clock
+        * update is in progress - during this time writes fail silently.
+        *
+        * Use mc146818_avoid_UIP() to avoid this.
+        */
+       if (!mc146818_avoid_UIP(cmos_set_alarm_callback, &p))
+               return -EIO;
 
        cmos->alarm_expires = rtc_tm_to_time64(&t->time);