[PATCH] shpchp: fix improper mmio mapping

Current SHPCHP driver seems not to map MMIO region properly. This
patch fixes this bug. This patch also cleanup the code.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index f5a8bf3..d82987f 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -791,7 +791,7 @@
 	}
 	if (php_ctlr->pci_dev) {
 		iounmap(php_ctlr->creg);
-		release_mem_region(pci_resource_start(php_ctlr->pci_dev, 0), pci_resource_len(php_ctlr->pci_dev, 0));
+		release_mem_region(ctrl->mmio_base, ctrl->mmio_size);
 		php_ctlr->pci_dev = NULL;
 	}
 
@@ -1320,19 +1320,34 @@
 	.check_cmd_status		= hpc_check_cmd_status,
 };
 
+inline static int shpc_indirect_creg_read(struct controller *ctrl, int index,
+					  u32 *value)
+{
+	int rc;
+	u32 cap_offset = ctrl->cap_offset;
+	struct pci_dev *pdev = ctrl->pci_dev;
+
+	rc = pci_write_config_byte(pdev, cap_offset + DWORD_SELECT, index);
+	if (rc)
+		return rc;
+	return pci_read_config_dword(pdev, cap_offset + DWORD_DATA, value);
+}
+
 int shpc_init(struct controller * ctrl, struct pci_dev * pdev)
 {
 	struct php_ctlr_state_s *php_ctlr, *p;
 	void *instance_id = ctrl;
-	int rc;
+	int rc, num_slots = 0;
 	u8 hp_slot;
 	static int first = 1;
-	u32 shpc_cap_offset, shpc_base_offset;
+	u32 shpc_base_offset;
 	u32 tempdword, slot_reg;
 	u8 i;
 
 	DBG_ENTER_ROUTINE
 
+	ctrl->pci_dev = pdev;  /* pci_dev of the P2P bridge */
+
 	spin_lock_init(&list_lock);
 	php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL);
 
@@ -1347,41 +1362,45 @@
 
 	if ((pdev->vendor == PCI_VENDOR_ID_AMD) || (pdev->device ==
 				PCI_DEVICE_ID_AMD_GOLAM_7450)) {
-		shpc_base_offset = 0;  /* amd shpc driver doesn't use this; assume 0 */
+		/* amd shpc driver doesn't use Base Offset; assume 0 */
+		ctrl->mmio_base = pci_resource_start(pdev, 0);
+		ctrl->mmio_size = pci_resource_len(pdev, 0);
 	} else {
-		if ((shpc_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_SHPC)) == 0) {
-			err("%s : shpc_cap_offset == 0\n", __FUNCTION__);
+		ctrl->cap_offset = pci_find_capability(pdev, PCI_CAP_ID_SHPC);
+		if (!ctrl->cap_offset) {
+			err("%s : cap_offset == 0\n", __FUNCTION__);
 			goto abort_free_ctlr;
 		}
-		dbg("%s: shpc_cap_offset = %x\n", __FUNCTION__, shpc_cap_offset);	
-	
-		rc = pci_write_config_byte(pdev, (u8)shpc_cap_offset + DWORD_SELECT , BASE_OFFSET);
+		dbg("%s: cap_offset = %x\n", __FUNCTION__, ctrl->cap_offset);
+
+		rc = shpc_indirect_creg_read(ctrl, 0, &shpc_base_offset);
 		if (rc) {
-			err("%s : pci_word_config_byte failed\n", __FUNCTION__);
-			goto abort_free_ctlr;
-		}
-	
-		rc = pci_read_config_dword(pdev, (u8)shpc_cap_offset + DWORD_DATA, &shpc_base_offset);
-		if (rc) {
-			err("%s : pci_read_config_dword failed\n", __FUNCTION__);
+			err("%s: cannot read base_offset\n", __FUNCTION__);
 			goto abort_free_ctlr;
 		}
 
-		for (i = 0; i <= 14; i++) {
-			rc = pci_write_config_byte(pdev, (u8)shpc_cap_offset +  DWORD_SELECT , i);
+		rc = shpc_indirect_creg_read(ctrl, 3, &tempdword);
+		if (rc) {
+			err("%s: cannot read slot config\n", __FUNCTION__);
+			goto abort_free_ctlr;
+		}
+		num_slots = tempdword & SLOT_NUM;
+		dbg("%s: num_slots (indirect) %x\n", __FUNCTION__, num_slots);
+
+		for (i = 0; i < 9 + num_slots; i++) {
+			rc = shpc_indirect_creg_read(ctrl, i, &tempdword);
 			if (rc) {
-				err("%s : pci_word_config_byte failed\n", __FUNCTION__);
-				goto abort_free_ctlr;
-			}
-	
-			rc = pci_read_config_dword(pdev, (u8)shpc_cap_offset + DWORD_DATA, &tempdword);
-			if (rc) {
-				err("%s : pci_read_config_dword failed\n", __FUNCTION__);
+				err("%s: cannot read creg (index = %d)\n",
+				    __FUNCTION__, i);
 				goto abort_free_ctlr;
 			}
 			dbg("%s: offset %d: value %x\n", __FUNCTION__,i,
 					tempdword);
 		}
+
+		ctrl->mmio_base =
+			pci_resource_start(pdev, 0) + shpc_base_offset;
+		ctrl->mmio_size = 0x24 + 0x4 * num_slots;
 	}
 
 	if (first) {
@@ -1395,16 +1414,16 @@
 	if (pci_enable_device(pdev))
 		goto abort_free_ctlr;
 
-	if (!request_mem_region(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0), MY_NAME)) {
+	if (!request_mem_region(ctrl->mmio_base, ctrl->mmio_size, MY_NAME)) {
 		err("%s: cannot reserve MMIO region\n", __FUNCTION__);
 		goto abort_free_ctlr;
 	}
 
-	php_ctlr->creg = ioremap(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0));
+	php_ctlr->creg = ioremap(ctrl->mmio_base, ctrl->mmio_size);
 	if (!php_ctlr->creg) {
-		err("%s: cannot remap MMIO region %lx @ %lx\n", __FUNCTION__, pci_resource_len(pdev, 0), 
-			pci_resource_start(pdev, 0) + shpc_base_offset);
-		release_mem_region(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0));
+		err("%s: cannot remap MMIO region %lx @ %lx\n", __FUNCTION__,
+		    ctrl->mmio_size, ctrl->mmio_base);
+		release_mem_region(ctrl->mmio_base, ctrl->mmio_size);
 		goto abort_free_ctlr;
 	}
 	dbg("%s: php_ctlr->creg %p\n", __FUNCTION__, php_ctlr->creg);