18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
48c2ecf20Sopenharmony_ci * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * All rights reserved.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Send feedback to <lxie@us.ibm.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <linux/pci.h>
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <asm/pci-bridge.h>
158c2ecf20Sopenharmony_ci#include <asm/rtas.h>
168c2ecf20Sopenharmony_ci#include <asm/machdep.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "../pci.h"		/* for pci_add_new_bus */
198c2ecf20Sopenharmony_ci#include "rpaphp.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ciint rpaphp_get_sensor_state(struct slot *slot, int *state)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	int rc;
248c2ecf20Sopenharmony_ci	int setlevel;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	rc = rtas_get_sensor(DR_ENTITY_SENSE, slot->index, state);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (rc < 0) {
298c2ecf20Sopenharmony_ci		if (rc == -EFAULT || rc == -EEXIST) {
308c2ecf20Sopenharmony_ci			dbg("%s: slot must be power up to get sensor-state\n",
318c2ecf20Sopenharmony_ci			    __func__);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci			/* some slots have to be powered up
348c2ecf20Sopenharmony_ci			 * before get-sensor will succeed.
358c2ecf20Sopenharmony_ci			 */
368c2ecf20Sopenharmony_ci			rc = rtas_set_power_level(slot->power_domain, POWER_ON,
378c2ecf20Sopenharmony_ci						  &setlevel);
388c2ecf20Sopenharmony_ci			if (rc < 0) {
398c2ecf20Sopenharmony_ci				dbg("%s: power on slot[%s] failed rc=%d.\n",
408c2ecf20Sopenharmony_ci				    __func__, slot->name, rc);
418c2ecf20Sopenharmony_ci			} else {
428c2ecf20Sopenharmony_ci				rc = rtas_get_sensor(DR_ENTITY_SENSE,
438c2ecf20Sopenharmony_ci						     slot->index, state);
448c2ecf20Sopenharmony_ci			}
458c2ecf20Sopenharmony_ci		} else if (rc == -ENODEV)
468c2ecf20Sopenharmony_ci			info("%s: slot is unusable\n", __func__);
478c2ecf20Sopenharmony_ci		else
488c2ecf20Sopenharmony_ci			err("%s failed to get sensor state\n", __func__);
498c2ecf20Sopenharmony_ci	}
508c2ecf20Sopenharmony_ci	return rc;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/**
548c2ecf20Sopenharmony_ci * rpaphp_enable_slot - record slot state, config pci device
558c2ecf20Sopenharmony_ci * @slot: target &slot
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci * Initialize values in the slot structure to indicate if there is a pci card
588c2ecf20Sopenharmony_ci * plugged into the slot. If the slot is not empty, run the pcibios routine
598c2ecf20Sopenharmony_ci * to get pcibios stuff correctly set up.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_ciint rpaphp_enable_slot(struct slot *slot)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	int rc, level, state;
648c2ecf20Sopenharmony_ci	struct pci_bus *bus;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	slot->state = EMPTY;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* Find out if the power is turned on for the slot */
698c2ecf20Sopenharmony_ci	rc = rtas_get_power_level(slot->power_domain, &level);
708c2ecf20Sopenharmony_ci	if (rc)
718c2ecf20Sopenharmony_ci		return rc;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Figure out if there is an adapter in the slot */
748c2ecf20Sopenharmony_ci	rc = rpaphp_get_sensor_state(slot, &state);
758c2ecf20Sopenharmony_ci	if (rc)
768c2ecf20Sopenharmony_ci		return rc;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	bus = pci_find_bus_by_node(slot->dn);
798c2ecf20Sopenharmony_ci	if (!bus) {
808c2ecf20Sopenharmony_ci		err("%s: no pci_bus for dn %pOF\n", __func__, slot->dn);
818c2ecf20Sopenharmony_ci		return -EINVAL;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	slot->bus = bus;
858c2ecf20Sopenharmony_ci	slot->pci_devs = &bus->devices;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* if there's an adapter in the slot, go add the pci devices */
888c2ecf20Sopenharmony_ci	if (state == PRESENT) {
898c2ecf20Sopenharmony_ci		slot->state = NOT_CONFIGURED;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		/* non-empty slot has to have child */
928c2ecf20Sopenharmony_ci		if (!slot->dn->child) {
938c2ecf20Sopenharmony_ci			err("%s: slot[%s]'s device_node doesn't have child for adapter\n",
948c2ecf20Sopenharmony_ci			    __func__, slot->name);
958c2ecf20Sopenharmony_ci			return -EINVAL;
968c2ecf20Sopenharmony_ci		}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		if (list_empty(&bus->devices)) {
998c2ecf20Sopenharmony_ci			pseries_eeh_init_edev_recursive(PCI_DN(slot->dn));
1008c2ecf20Sopenharmony_ci			pci_hp_add_devices(bus);
1018c2ecf20Sopenharmony_ci		}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		if (!list_empty(&bus->devices)) {
1048c2ecf20Sopenharmony_ci			slot->state = CONFIGURED;
1058c2ecf20Sopenharmony_ci		}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		if (rpaphp_debug) {
1088c2ecf20Sopenharmony_ci			struct pci_dev *dev;
1098c2ecf20Sopenharmony_ci			dbg("%s: pci_devs of slot[%pOF]\n", __func__, slot->dn);
1108c2ecf20Sopenharmony_ci			list_for_each_entry(dev, &bus->devices, bus_list)
1118c2ecf20Sopenharmony_ci				dbg("\t%s\n", pci_name(dev));
1128c2ecf20Sopenharmony_ci		}
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
117