18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Sonics Silicon Backplane
38c2ecf20Sopenharmony_ci * PCI Hostdevice wrapper
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>
68c2ecf20Sopenharmony_ci * Copyright (c) 2005 Stefano Brivio <st3@riseup.net>
78c2ecf20Sopenharmony_ci * Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
88c2ecf20Sopenharmony_ci * Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
98c2ecf20Sopenharmony_ci * Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/pm.h>
158c2ecf20Sopenharmony_ci#include <linux/pci.h>
168c2ecf20Sopenharmony_ci#include <linux/export.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/ssb/ssb.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
228c2ecf20Sopenharmony_cistatic int ssb_pcihost_suspend(struct device *d)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct pci_dev *dev = to_pci_dev(d);
258c2ecf20Sopenharmony_ci	struct ssb_bus *ssb = pci_get_drvdata(dev);
268c2ecf20Sopenharmony_ci	int err;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	err = ssb_bus_suspend(ssb);
298c2ecf20Sopenharmony_ci	if (err)
308c2ecf20Sopenharmony_ci		return err;
318c2ecf20Sopenharmony_ci	pci_save_state(dev);
328c2ecf20Sopenharmony_ci	pci_disable_device(dev);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* if there is a wakeup enabled child device on ssb bus,
358c2ecf20Sopenharmony_ci	   enable pci wakeup posibility. */
368c2ecf20Sopenharmony_ci	device_set_wakeup_enable(d, d->power.wakeup_path);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	pci_prepare_to_sleep(dev);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	return 0;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int ssb_pcihost_resume(struct device *d)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct pci_dev *dev = to_pci_dev(d);
468c2ecf20Sopenharmony_ci	struct ssb_bus *ssb = pci_get_drvdata(dev);
478c2ecf20Sopenharmony_ci	int err;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	pci_back_from_sleep(dev);
508c2ecf20Sopenharmony_ci	err = pci_enable_device(dev);
518c2ecf20Sopenharmony_ci	if (err)
528c2ecf20Sopenharmony_ci		return err;
538c2ecf20Sopenharmony_ci	pci_restore_state(dev);
548c2ecf20Sopenharmony_ci	err = ssb_bus_resume(ssb);
558c2ecf20Sopenharmony_ci	if (err)
568c2ecf20Sopenharmony_ci		return err;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return 0;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct dev_pm_ops ssb_pcihost_pm_ops = {
628c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(ssb_pcihost_suspend, ssb_pcihost_resume)
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int ssb_pcihost_probe(struct pci_dev *dev,
688c2ecf20Sopenharmony_ci			     const struct pci_device_id *id)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct ssb_bus *ssb;
718c2ecf20Sopenharmony_ci	int err = -ENOMEM;
728c2ecf20Sopenharmony_ci	const char *name;
738c2ecf20Sopenharmony_ci	u32 val;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
768c2ecf20Sopenharmony_ci	if (!ssb)
778c2ecf20Sopenharmony_ci		goto out;
788c2ecf20Sopenharmony_ci	err = pci_enable_device(dev);
798c2ecf20Sopenharmony_ci	if (err)
808c2ecf20Sopenharmony_ci		goto err_kfree_ssb;
818c2ecf20Sopenharmony_ci	name = dev_name(&dev->dev);
828c2ecf20Sopenharmony_ci	if (dev->driver && dev->driver->name)
838c2ecf20Sopenharmony_ci		name = dev->driver->name;
848c2ecf20Sopenharmony_ci	err = pci_request_regions(dev, name);
858c2ecf20Sopenharmony_ci	if (err)
868c2ecf20Sopenharmony_ci		goto err_pci_disable;
878c2ecf20Sopenharmony_ci	pci_set_master(dev);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* Disable the RETRY_TIMEOUT register (0x41) to keep
908c2ecf20Sopenharmony_ci	 * PCI Tx retries from interfering with C3 CPU state */
918c2ecf20Sopenharmony_ci	pci_read_config_dword(dev, 0x40, &val);
928c2ecf20Sopenharmony_ci	if ((val & 0x0000ff00) != 0)
938c2ecf20Sopenharmony_ci		pci_write_config_dword(dev, 0x40, val & 0xffff00ff);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	err = ssb_bus_pcibus_register(ssb, dev);
968c2ecf20Sopenharmony_ci	if (err)
978c2ecf20Sopenharmony_ci		goto err_pci_release_regions;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, ssb);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ciout:
1028c2ecf20Sopenharmony_ci	return err;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cierr_pci_release_regions:
1058c2ecf20Sopenharmony_ci	pci_release_regions(dev);
1068c2ecf20Sopenharmony_cierr_pci_disable:
1078c2ecf20Sopenharmony_ci	pci_disable_device(dev);
1088c2ecf20Sopenharmony_cierr_kfree_ssb:
1098c2ecf20Sopenharmony_ci	kfree(ssb);
1108c2ecf20Sopenharmony_ci	return err;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic void ssb_pcihost_remove(struct pci_dev *dev)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct ssb_bus *ssb = pci_get_drvdata(dev);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	ssb_bus_unregister(ssb);
1188c2ecf20Sopenharmony_ci	pci_release_regions(dev);
1198c2ecf20Sopenharmony_ci	pci_disable_device(dev);
1208c2ecf20Sopenharmony_ci	kfree(ssb);
1218c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, NULL);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ciint ssb_pcihost_register(struct pci_driver *driver)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	driver->probe = ssb_pcihost_probe;
1278c2ecf20Sopenharmony_ci	driver->remove = ssb_pcihost_remove;
1288c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
1298c2ecf20Sopenharmony_ci	driver->driver.pm = &ssb_pcihost_pm_ops;
1308c2ecf20Sopenharmony_ci#endif
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return pci_register_driver(driver);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_pcihost_register);
135