/*
  * Freescale MPC85xx/MPC86xx RapidIO support
  *
+ * Copyright 2009 Sysgo AG
+ * Thomas Moll <thomas.moll@sysgo.com>
+ * - fixed maintenance access routines, check for aligned access
+ *
  * Copyright 2009 Integrated Device Technology, Inc.
  * Alex Bounine <alexandre.bounine@idt.com>
  * - Added Port-Write message handling
        pr_debug
            ("fsl_rio_config_read: index %d destid %d hopcount %d offset %8.8x len %d\n",
             index, destid, hopcount, offset, len);
+
+       /* 16MB maintenance window possible */
+       /* allow only aligned access to maintenance registers */
+       if (offset > (0x1000000 - len) || !IS_ALIGNED(offset, len))
+               return -EINVAL;
+
        out_be32(&priv->maint_atmu_regs->rowtar,
-                (destid << 22) | (hopcount << 12) | ((offset & ~0x3) >> 9));
+                (destid << 22) | (hopcount << 12) | (offset >> 12));
+       out_be32(&priv->maint_atmu_regs->rowtear,  (destid >> 10));
 
-       data = (u8 *) priv->maint_win + offset;
+       data = (u8 *) priv->maint_win + (offset & (RIO_MAINT_WIN_SIZE - 1));
        switch (len) {
        case 1:
                __fsl_read_rio_config(rval, data, err, "lbz");
        case 2:
                __fsl_read_rio_config(rval, data, err, "lhz");
                break;
-       default:
+       case 4:
                __fsl_read_rio_config(rval, data, err, "lwz");
                break;
+       default:
+               return -EINVAL;
        }
 
        if (err) {
        pr_debug
            ("fsl_rio_config_write: index %d destid %d hopcount %d offset %8.8x len %d val %8.8x\n",
             index, destid, hopcount, offset, len, val);
+
+       /* 16MB maintenance windows possible */
+       /* allow only aligned access to maintenance registers */
+       if (offset > (0x1000000 - len) || !IS_ALIGNED(offset, len))
+               return -EINVAL;
+
        out_be32(&priv->maint_atmu_regs->rowtar,
-                (destid << 22) | (hopcount << 12) | ((offset & ~0x3) >> 9));
+                (destid << 22) | (hopcount << 12) | (offset >> 12));
+       out_be32(&priv->maint_atmu_regs->rowtear,  (destid >> 10));
 
-       data = (u8 *) priv->maint_win + offset;
+       data = (u8 *) priv->maint_win + (offset & (RIO_MAINT_WIN_SIZE - 1));
        switch (len) {
        case 1:
                out_8((u8 *) data, val);
        case 2:
                out_be16((u16 *) data, val);
                break;
-       default:
+       case 4:
                out_be32((u32 *) data, val);
                break;
+       default:
+               return -EINVAL;
        }
 
        return 0;
 
        /* Configure maintenance transaction window */
        out_be32(&priv->maint_atmu_regs->rowbar, law_start >> 12);
-       out_be32(&priv->maint_atmu_regs->rowar, 0x80077015);    /* 4M */
+       out_be32(&priv->maint_atmu_regs->rowar,
+                0x80077000 | (ilog2(RIO_MAINT_WIN_SIZE) - 1));
 
        priv->maint_win = ioremap(law_start, RIO_MAINT_WIN_SIZE);