xref: /openbmc/linux/drivers/ssb/pcihost_wrapper.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
161e115a5SMichael Buesch /*
261e115a5SMichael Buesch  * Sonics Silicon Backplane
361e115a5SMichael Buesch  * PCI Hostdevice wrapper
461e115a5SMichael Buesch  *
561e115a5SMichael Buesch  * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>
661e115a5SMichael Buesch  * Copyright (c) 2005 Stefano Brivio <st3@riseup.net>
761e115a5SMichael Buesch  * Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
861e115a5SMichael Buesch  * Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
9eb032b98SMichael Büsch  * Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
1061e115a5SMichael Buesch  *
1161e115a5SMichael Buesch  * Licensed under the GNU/GPL. See COPYING for details.
1261e115a5SMichael Buesch  */
1361e115a5SMichael Buesch 
145580373fSAndrey Skvortsov #include <linux/pm.h>
1561e115a5SMichael Buesch #include <linux/pci.h>
161014c22eSPaul Gortmaker #include <linux/export.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
1861e115a5SMichael Buesch #include <linux/ssb/ssb.h>
1961e115a5SMichael Buesch 
2061e115a5SMichael Buesch 
215580373fSAndrey Skvortsov #ifdef CONFIG_PM_SLEEP
ssb_pcihost_suspend(struct device * d)225580373fSAndrey Skvortsov static int ssb_pcihost_suspend(struct device *d)
2361e115a5SMichael Buesch {
245580373fSAndrey Skvortsov 	struct pci_dev *dev = to_pci_dev(d);
258fe2b65aSMichael Buesch 	struct ssb_bus *ssb = pci_get_drvdata(dev);
268fe2b65aSMichael Buesch 	int err;
278fe2b65aSMichael Buesch 
288fe2b65aSMichael Buesch 	err = ssb_bus_suspend(ssb);
298fe2b65aSMichael Buesch 	if (err)
308fe2b65aSMichael Buesch 		return err;
3161e115a5SMichael Buesch 	pci_save_state(dev);
3261e115a5SMichael Buesch 	pci_disable_device(dev);
335580373fSAndrey Skvortsov 
345580373fSAndrey Skvortsov 	/* if there is a wakeup enabled child device on ssb bus,
355580373fSAndrey Skvortsov 	   enable pci wakeup posibility. */
365580373fSAndrey Skvortsov 	device_set_wakeup_enable(d, d->power.wakeup_path);
375580373fSAndrey Skvortsov 
385580373fSAndrey Skvortsov 	pci_prepare_to_sleep(dev);
3961e115a5SMichael Buesch 
4061e115a5SMichael Buesch 	return 0;
4161e115a5SMichael Buesch }
4261e115a5SMichael Buesch 
ssb_pcihost_resume(struct device * d)435580373fSAndrey Skvortsov static int ssb_pcihost_resume(struct device *d)
4461e115a5SMichael Buesch {
455580373fSAndrey Skvortsov 	struct pci_dev *dev = to_pci_dev(d);
468fe2b65aSMichael Buesch 	struct ssb_bus *ssb = pci_get_drvdata(dev);
4761e115a5SMichael Buesch 	int err;
4861e115a5SMichael Buesch 
495580373fSAndrey Skvortsov 	pci_back_from_sleep(dev);
5061e115a5SMichael Buesch 	err = pci_enable_device(dev);
5161e115a5SMichael Buesch 	if (err)
5261e115a5SMichael Buesch 		return err;
5361e115a5SMichael Buesch 	pci_restore_state(dev);
548fe2b65aSMichael Buesch 	err = ssb_bus_resume(ssb);
558fe2b65aSMichael Buesch 	if (err)
568fe2b65aSMichael Buesch 		return err;
5761e115a5SMichael Buesch 
5861e115a5SMichael Buesch 	return 0;
5961e115a5SMichael Buesch }
605580373fSAndrey Skvortsov 
615580373fSAndrey Skvortsov static const struct dev_pm_ops ssb_pcihost_pm_ops = {
625580373fSAndrey Skvortsov 	SET_SYSTEM_SLEEP_PM_OPS(ssb_pcihost_suspend, ssb_pcihost_resume)
635580373fSAndrey Skvortsov };
645580373fSAndrey Skvortsov 
655580373fSAndrey Skvortsov #endif /* CONFIG_PM_SLEEP */
6661e115a5SMichael Buesch 
ssb_pcihost_probe(struct pci_dev * dev,const struct pci_device_id * id)67163247c1SGreg Kroah-Hartman static int ssb_pcihost_probe(struct pci_dev *dev,
6861e115a5SMichael Buesch 			     const struct pci_device_id *id)
6961e115a5SMichael Buesch {
7061e115a5SMichael Buesch 	struct ssb_bus *ssb;
7161e115a5SMichael Buesch 	int err = -ENOMEM;
72e081685cSLarry Finger 	u32 val;
7361e115a5SMichael Buesch 
7461e115a5SMichael Buesch 	ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
7561e115a5SMichael Buesch 	if (!ssb)
7661e115a5SMichael Buesch 		goto out;
7761e115a5SMichael Buesch 	err = pci_enable_device(dev);
7861e115a5SMichael Buesch 	if (err)
7961e115a5SMichael Buesch 		goto err_kfree_ssb;
80*7c3b2c93SUwe Kleine-König 	err = pci_request_regions(dev, dev_driver_string(&dev->dev));
8161e115a5SMichael Buesch 	if (err)
8261e115a5SMichael Buesch 		goto err_pci_disable;
8361e115a5SMichael Buesch 	pci_set_master(dev);
8461e115a5SMichael Buesch 
85e081685cSLarry Finger 	/* Disable the RETRY_TIMEOUT register (0x41) to keep
86e081685cSLarry Finger 	 * PCI Tx retries from interfering with C3 CPU state */
87e081685cSLarry Finger 	pci_read_config_dword(dev, 0x40, &val);
88e081685cSLarry Finger 	if ((val & 0x0000ff00) != 0)
89e081685cSLarry Finger 		pci_write_config_dword(dev, 0x40, val & 0xffff00ff);
90e081685cSLarry Finger 
9161e115a5SMichael Buesch 	err = ssb_bus_pcibus_register(ssb, dev);
9261e115a5SMichael Buesch 	if (err)
9361e115a5SMichael Buesch 		goto err_pci_release_regions;
9461e115a5SMichael Buesch 
9561e115a5SMichael Buesch 	pci_set_drvdata(dev, ssb);
9661e115a5SMichael Buesch 
9761e115a5SMichael Buesch out:
9861e115a5SMichael Buesch 	return err;
9961e115a5SMichael Buesch 
10061e115a5SMichael Buesch err_pci_release_regions:
10161e115a5SMichael Buesch 	pci_release_regions(dev);
10261e115a5SMichael Buesch err_pci_disable:
10361e115a5SMichael Buesch 	pci_disable_device(dev);
10461e115a5SMichael Buesch err_kfree_ssb:
10561e115a5SMichael Buesch 	kfree(ssb);
10661e115a5SMichael Buesch 	return err;
10761e115a5SMichael Buesch }
10861e115a5SMichael Buesch 
ssb_pcihost_remove(struct pci_dev * dev)10961e115a5SMichael Buesch static void ssb_pcihost_remove(struct pci_dev *dev)
11061e115a5SMichael Buesch {
11161e115a5SMichael Buesch 	struct ssb_bus *ssb = pci_get_drvdata(dev);
11261e115a5SMichael Buesch 
11361e115a5SMichael Buesch 	ssb_bus_unregister(ssb);
11461e115a5SMichael Buesch 	pci_release_regions(dev);
11561e115a5SMichael Buesch 	pci_disable_device(dev);
11661e115a5SMichael Buesch 	kfree(ssb);
11761e115a5SMichael Buesch 	pci_set_drvdata(dev, NULL);
11861e115a5SMichael Buesch }
11961e115a5SMichael Buesch 
ssb_pcihost_register(struct pci_driver * driver)120163247c1SGreg Kroah-Hartman int ssb_pcihost_register(struct pci_driver *driver)
12161e115a5SMichael Buesch {
12261e115a5SMichael Buesch 	driver->probe = ssb_pcihost_probe;
12361e115a5SMichael Buesch 	driver->remove = ssb_pcihost_remove;
1245580373fSAndrey Skvortsov #ifdef CONFIG_PM_SLEEP
1255580373fSAndrey Skvortsov 	driver->driver.pm = &ssb_pcihost_pm_ops;
1265580373fSAndrey Skvortsov #endif
12761e115a5SMichael Buesch 
12861e115a5SMichael Buesch 	return pci_register_driver(driver);
12961e115a5SMichael Buesch }
13061e115a5SMichael Buesch EXPORT_SYMBOL(ssb_pcihost_register);
131