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