|
@@ -15,6 +15,9 @@
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
+#include <linux/list.h>
|
|
|
|
+#include <linux/pm.h>
|
|
|
|
+#include <asm/olpc.h>
|
|
|
|
|
|
/*
|
|
/*
|
|
* The default port config.
|
|
* The default port config.
|
|
@@ -28,6 +31,19 @@ static struct via_port_cfg adap_configs[] = {
|
|
{ 0, 0, 0, 0 }
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * The OLPC XO-1.5 puts the camera power and reset lines onto
|
|
|
|
+ * GPIO 2C.
|
|
|
|
+ */
|
|
|
|
+static const struct via_port_cfg olpc_adap_configs[] = {
|
|
|
|
+ [VIA_PORT_26] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x26 },
|
|
|
|
+ [VIA_PORT_31] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x31 },
|
|
|
|
+ [VIA_PORT_25] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 },
|
|
|
|
+ [VIA_PORT_2C] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x2c },
|
|
|
|
+ [VIA_PORT_3D] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d },
|
|
|
|
+ { 0, 0, 0, 0 }
|
|
|
|
+};
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* We currently only support one viafb device (will there ever be
|
|
* We currently only support one viafb device (will there ever be
|
|
* more than one?), so just declare it globally here.
|
|
* more than one?), so just declare it globally here.
|
|
@@ -575,6 +591,78 @@ static void via_teardown_subdevs(void)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Power management functions
|
|
|
|
+ */
|
|
|
|
+#ifdef CONFIG_PM
|
|
|
|
+static LIST_HEAD(viafb_pm_hooks);
|
|
|
|
+static DEFINE_MUTEX(viafb_pm_hooks_lock);
|
|
|
|
+
|
|
|
|
+void viafb_pm_register(struct viafb_pm_hooks *hooks)
|
|
|
|
+{
|
|
|
|
+ INIT_LIST_HEAD(&hooks->list);
|
|
|
|
+
|
|
|
|
+ mutex_lock(&viafb_pm_hooks_lock);
|
|
|
|
+ list_add_tail(&hooks->list, &viafb_pm_hooks);
|
|
|
|
+ mutex_unlock(&viafb_pm_hooks_lock);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(viafb_pm_register);
|
|
|
|
+
|
|
|
|
+void viafb_pm_unregister(struct viafb_pm_hooks *hooks)
|
|
|
|
+{
|
|
|
|
+ mutex_lock(&viafb_pm_hooks_lock);
|
|
|
|
+ list_del(&hooks->list);
|
|
|
|
+ mutex_unlock(&viafb_pm_hooks_lock);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(viafb_pm_unregister);
|
|
|
|
+
|
|
|
|
+static int via_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
|
+{
|
|
|
|
+ struct viafb_pm_hooks *hooks;
|
|
|
|
+
|
|
|
|
+ if (state.event != PM_EVENT_SUSPEND)
|
|
|
|
+ return 0;
|
|
|
|
+ /*
|
|
|
|
+ * "I've occasionally hit a few drivers that caused suspend
|
|
|
|
+ * failures, and each and every time it was a driver bug, and
|
|
|
|
+ * the right thing to do was to just ignore the error and suspend
|
|
|
|
+ * anyway - returning an error code and trying to undo the suspend
|
|
|
|
+ * is not what anybody ever really wants, even if our model
|
|
|
|
+ *_allows_ for it."
|
|
|
|
+ * -- Linus Torvalds, Dec. 7, 2009
|
|
|
|
+ */
|
|
|
|
+ mutex_lock(&viafb_pm_hooks_lock);
|
|
|
|
+ list_for_each_entry_reverse(hooks, &viafb_pm_hooks, list)
|
|
|
|
+ hooks->suspend(hooks->private);
|
|
|
|
+ mutex_unlock(&viafb_pm_hooks_lock);
|
|
|
|
+
|
|
|
|
+ pci_save_state(pdev);
|
|
|
|
+ pci_disable_device(pdev);
|
|
|
|
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int via_resume(struct pci_dev *pdev)
|
|
|
|
+{
|
|
|
|
+ struct viafb_pm_hooks *hooks;
|
|
|
|
+
|
|
|
|
+ /* Get the bus side powered up */
|
|
|
|
+ pci_set_power_state(pdev, PCI_D0);
|
|
|
|
+ pci_restore_state(pdev);
|
|
|
|
+ if (pci_enable_device(pdev))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ pci_set_master(pdev);
|
|
|
|
+
|
|
|
|
+ /* Now bring back any subdevs */
|
|
|
|
+ mutex_lock(&viafb_pm_hooks_lock);
|
|
|
|
+ list_for_each_entry(hooks, &viafb_pm_hooks, list)
|
|
|
|
+ hooks->resume(hooks->private);
|
|
|
|
+ mutex_unlock(&viafb_pm_hooks_lock);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+#endif /* CONFIG_PM */
|
|
|
|
|
|
static int __devinit via_pci_probe(struct pci_dev *pdev,
|
|
static int __devinit via_pci_probe(struct pci_dev *pdev,
|
|
const struct pci_device_id *ent)
|
|
const struct pci_device_id *ent)
|
|
@@ -584,6 +672,7 @@ static int __devinit via_pci_probe(struct pci_dev *pdev,
|
|
ret = pci_enable_device(pdev);
|
|
ret = pci_enable_device(pdev);
|
|
if (ret)
|
|
if (ret)
|
|
return ret;
|
|
return ret;
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Global device initialization.
|
|
* Global device initialization.
|
|
*/
|
|
*/
|
|
@@ -591,6 +680,9 @@ static int __devinit via_pci_probe(struct pci_dev *pdev,
|
|
global_dev.pdev = pdev;
|
|
global_dev.pdev = pdev;
|
|
global_dev.chip_type = ent->driver_data;
|
|
global_dev.chip_type = ent->driver_data;
|
|
global_dev.port_cfg = adap_configs;
|
|
global_dev.port_cfg = adap_configs;
|
|
|
|
+ if (machine_is_olpc())
|
|
|
|
+ global_dev.port_cfg = olpc_adap_configs;
|
|
|
|
+
|
|
spin_lock_init(&global_dev.reg_lock);
|
|
spin_lock_init(&global_dev.reg_lock);
|
|
ret = via_pci_setup_mmio(&global_dev);
|
|
ret = via_pci_setup_mmio(&global_dev);
|
|
if (ret)
|
|
if (ret)
|
|
@@ -663,8 +755,8 @@ static struct pci_driver via_driver = {
|
|
.probe = via_pci_probe,
|
|
.probe = via_pci_probe,
|
|
.remove = __devexit_p(via_pci_remove),
|
|
.remove = __devexit_p(via_pci_remove),
|
|
#ifdef CONFIG_PM
|
|
#ifdef CONFIG_PM
|
|
- .suspend = viafb_suspend,
|
|
|
|
- .resume = viafb_resume,
|
|
|
|
|
|
+ .suspend = via_suspend,
|
|
|
|
+ .resume = via_resume,
|
|
#endif
|
|
#endif
|
|
};
|
|
};
|
|
|
|
|