mcb-lpc: Reallocate memory region to avoid memory overlapping
[linux-2.6-microblaze.git] / drivers / mcb / mcb-lpc.c
index 53decd8..a851e02 100644 (file)
@@ -23,7 +23,7 @@ static int mcb_lpc_probe(struct platform_device *pdev)
 {
        struct resource *res;
        struct priv *priv;
-       int ret = 0;
+       int ret = 0, table_size;
 
        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -58,16 +58,43 @@ static int mcb_lpc_probe(struct platform_device *pdev)
 
        ret = chameleon_parse_cells(priv->bus, priv->mem->start, priv->base);
        if (ret < 0) {
-               mcb_release_bus(priv->bus);
-               return ret;
+               goto out_mcb_bus;
        }
 
-       dev_dbg(&pdev->dev, "Found %d cells\n", ret);
+       table_size = ret;
+
+       if (table_size < CHAM_HEADER_SIZE) {
+               /* Release the previous resources */
+               devm_iounmap(&pdev->dev, priv->base);
+               devm_release_mem_region(&pdev->dev, priv->mem->start, resource_size(priv->mem));
+
+               /* Then, allocate it again with the actual chameleon table size */
+               res = devm_request_mem_region(&pdev->dev, priv->mem->start,
+                                             table_size,
+                                             KBUILD_MODNAME);
+               if (!res) {
+                       dev_err(&pdev->dev, "Failed to request PCI memory\n");
+                       ret = -EBUSY;
+                       goto out_mcb_bus;
+               }
+
+               priv->base = devm_ioremap(&pdev->dev, priv->mem->start, table_size);
+               if (!priv->base) {
+                       dev_err(&pdev->dev, "Cannot ioremap\n");
+                       ret = -ENOMEM;
+                       goto out_mcb_bus;
+               }
+
+               platform_set_drvdata(pdev, priv);
+       }
 
        mcb_bus_add_devices(priv->bus);
 
        return 0;
 
+out_mcb_bus:
+       mcb_release_bus(priv->bus);
+       return ret;
 }
 
 static int mcb_lpc_remove(struct platform_device *pdev)