static int check_of_data(struct fsl_elbc_gpcm *priv, struct resource *res, u32 reg_br, u32 reg_or) { /* check specified bank */ if (priv->bank >= MAX_BANKS) { dev_err(priv->dev, "invalid bank\n"); return -ENODEV; } /* check specified mode (BR_MS_GPCM is 0) */ if ((reg_br & BR_MSEL) != BR_MS_GPCM) { dev_err(priv->dev, "unsupported mode\n"); return -ENODEV; } /* check specified mask vs. resource size */ if ((~(reg_or & OR_GPCM_AM) + 1) != resource_size(res)) { dev_err(priv->dev, "address mask / size mismatch\n"); return -ENODEV; } /* check specified address */ if ((reg_br & reg_or & BR_BA) != fsl_lbc_addr(res->start)) { dev_err(priv->dev, "base address mismatch\n"); return -ENODEV; } return 0; }
/** * fsl_lbc_find - find Localbus bank * @addr_base: base address of the memory bank * * This function walks LBC banks comparing "Base address" field of the BR * registers with the supplied addr_base argument. When bases match this * function returns bank number (starting with 0), otherwise it returns * appropriate errno value. */ int fsl_lbc_find(phys_addr_t addr_base) { int i; struct fsl_lbc_regs __iomem *lbc; if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) return -ENODEV; lbc = fsl_lbc_ctrl_dev->regs; for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) { u32 br = in_be32(&lbc->bank[i].br); u32 or = in_be32(&lbc->bank[i].or); if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base)) return i; } return -ENOENT; }
static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct fsl_elbc_gpcm *priv; struct uio_info *info; char *uio_name = NULL; struct resource res; unsigned int irq; u32 reg_br_cur; u32 reg_or_cur; u32 reg_br_new; u32 reg_or_new; int ret; if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) return -ENODEV; /* allocate private data */ priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = &pdev->dev; priv->lbc = fsl_lbc_ctrl_dev->regs; /* get device tree data */ ret = get_of_data(priv, node, &res, ®_br_new, ®_or_new, &irq, &uio_name); if (ret) goto out_err0; /* allocate UIO structure */ info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { ret = -ENOMEM; goto out_err0; } /* get current BR/OR values */ reg_br_cur = in_be32(&priv->lbc->bank[priv->bank].br); reg_or_cur = in_be32(&priv->lbc->bank[priv->bank].or); /* if bank already configured, make sure it matches */ if ((reg_br_cur & BR_V)) { if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM || (reg_br_cur & reg_or_cur & BR_BA) != fsl_lbc_addr(res.start)) { dev_err(priv->dev, "bank in use by another peripheral\n"); ret = -ENODEV; goto out_err1; } /* warn if behavior settings changing */ if ((reg_br_cur & ~(BR_BA | BR_V)) != (reg_br_new & ~(BR_BA | BR_V))) { dev_warn(priv->dev, "modifying BR settings: 0x%08x -> 0x%08x", reg_br_cur, reg_br_new); } if ((reg_or_cur & ~OR_GPCM_AM) != (reg_or_new & ~OR_GPCM_AM)) { dev_warn(priv->dev, "modifying OR settings: 0x%08x -> 0x%08x", reg_or_cur, reg_or_new); } } /* configure the bank (force base address and GPCM) */ reg_br_new &= ~(BR_BA | BR_MSEL); reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V; out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new); out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new); /* map the memory resource */ info->mem[0].internal_addr = ioremap(res.start, resource_size(&res)); if (!info->mem[0].internal_addr) { dev_err(priv->dev, "failed to map chip region\n"); ret = -ENODEV; goto out_err1; } /* set all UIO data */ if (node->name) info->mem[0].name = kstrdup(node->name, GFP_KERNEL); info->mem[0].addr = res.start; info->mem[0].size = resource_size(&res); info->mem[0].memtype = UIO_MEM_PHYS; info->priv = priv; info->name = uio_name; info->version = "0.0.1"; if (irq != NO_IRQ) { if (priv->irq_handler) { info->irq = irq; info->irq_flags = IRQF_SHARED; info->handler = priv->irq_handler; } else { irq = NO_IRQ; dev_warn(priv->dev, "ignoring irq, no handler\n"); } } if (priv->init) priv->init(info); /* register UIO device */ if (uio_register_device(priv->dev, info) != 0) { dev_err(priv->dev, "UIO registration failed\n"); ret = -ENODEV; goto out_err2; } /* store private data */ platform_set_drvdata(pdev, info); /* create sysfs files */ ret = device_create_file(priv->dev, &dev_attr_reg_br); if (ret) goto out_err3; ret = device_create_file(priv->dev, &dev_attr_reg_or); if (ret) goto out_err4; dev_info(priv->dev, "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n", priv->name, (unsigned long long)res.start, priv->bank, irq != NO_IRQ ? irq : -1); return 0; out_err4: device_remove_file(priv->dev, &dev_attr_reg_br); out_err3: platform_set_drvdata(pdev, NULL); uio_unregister_device(info); out_err2: if (priv->shutdown) priv->shutdown(info, true); iounmap(info->mem[0].internal_addr); out_err1: kfree(info->mem[0].name); kfree(info); out_err0: kfree(uio_name); kfree(priv); return ret; }