aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-s3c6410/om-3d7k-features.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-s3c6410/om-3d7k-features.c')
-rw-r--r--arch/arm/mach-s3c6410/om-3d7k-features.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c6410/om-3d7k-features.c b/arch/arm/mach-s3c6410/om-3d7k-features.c
new file mode 100644
index 00000000000..9136fc99f56
--- /dev/null
+++ b/arch/arm/mach-s3c6410/om-3d7k-features.c
@@ -0,0 +1,348 @@
+/*
+ * Support for features of Openmoko 3D7K
+ *
+ * (C) 2008 by Openmoko Inc.
+ * Author: Andy Green <andy@openmoko.com>
+ * All rights reserved.
+ *
+ * Somewhat based on the GTA01 / 02 neo1973_pm_ stuff mainly by Harald Welte
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <mach/hardware.h>
+#include <mach/om-3d7k.h>
+#include <asm/mach-types.h>
+
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/gpio.h>
+#include <linux/mmc/host.h>
+
+#include <plat/sdhci.h>
+#include <plat/devs.h>
+
+#include <plat/gpio-cfg.h>
+
+enum feature {
+ OM_3D7K_GPS, /* power to GPS section and LNA */
+ OM_3D7K_WLAN_BT, /* WLAN and BT Module */
+ OM_3D7K_GSM, /* GSM module */
+ OM_3D7K_USBHOST, /* USB Host power generation */
+ OM_3D7K_VIB, /* Vibrator */
+
+ OM_3D7K_FEATURE_COUNT /* always last */
+};
+
+
+struct om_3d7k_feature_info {
+ const char * name;
+ int depower_on_suspend;
+ int on;
+};
+
+static struct om_3d7k_feature_info feature_info[OM_3D7K_FEATURE_COUNT] = {
+ [OM_3D7K_GPS] = { "gps_power", 1, 0 },
+ [OM_3D7K_WLAN_BT] = { "wlan_bt_power", 1, 0 },
+ [OM_3D7K_GSM] = { "gsm_power", 0, 0 },
+ [OM_3D7K_USBHOST] = { "usbhost_power", 1, 0 },
+ [OM_3D7K_VIB] = { "vibrator_power", 1, 0 },
+};
+
+static struct regulator *gps_regulator;
+
+
+
+static void om_3d7k_features_pwron_set_on(enum feature feature)
+{
+ int gpio;
+
+ switch (feature) {
+ case OM_3D7K_GPS:
+ regulator_enable(gps_regulator);
+ /* enable LNA */
+ gpio_direction_output(OM_3D7K_GPIO_GPS_LNA_EN, 1);
+ break;
+ case OM_3D7K_WLAN_BT:
+
+ for (gpio = S3C64XX_GPH(0); gpio < S3C64XX_GPH(6); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2)); /* sdio */
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_UP);
+ }
+ /* assert reset */
+ s3c_gpio_setpull(OM_3D7K_GPIO_WLAN_RESET, S3C_GPIO_PULL_NONE);
+ s3c_gpio_cfgpin(OM_3D7K_GPIO_WLAN_RESET, S3C_GPIO_SFN(1));
+ gpio_direction_output(OM_3D7K_GPIO_WLAN_RESET, 0);
+
+ /* "full power down (active low)" -- deassert it*/
+ gpio_direction_output(OM_3D7K_GPIO_WLAN_PWRDN, 1);
+ s3c_gpio_setpull(OM_3D7K_GPIO_WLAN_PWRDN, S3C_GPIO_PULL_NONE);
+ s3c_gpio_cfgpin(OM_3D7K_GPIO_WLAN_PWRDN, S3C_GPIO_SFN(1));
+
+ /* enable P-Channel mosfet switch for power */
+ gpio_direction_output(OM_3D7K_GPIO_NWLAN_POWER, 0);
+ s3c_gpio_setpull(OM_3D7K_GPIO_NWLAN_POWER, S3C_GPIO_PULL_NONE);
+ s3c_gpio_cfgpin(OM_3D7K_GPIO_NWLAN_POWER, S3C_GPIO_SFN(1));
+ msleep(50);
+ /* deassert reset */
+ gpio_direction_output(OM_3D7K_GPIO_WLAN_RESET, 1);
+ msleep(1500);
+ sdhci_s3c_force_presence_change(&s3c_device_hsmmc1);
+ break;
+ case OM_3D7K_GSM:
+ /* give power to GSM module */
+ s3c_gpio_setpull(OM_3D7K_GPIO_N_MODEM_RESET, S3C_GPIO_PULL_NONE);
+ s3c_gpio_setpull(OM_3D7K_GPIO_MODEM_ON, S3C_GPIO_PULL_NONE);
+ s3c_gpio_cfgpin(OM_3D7K_GPIO_N_MODEM_RESET, S3C_GPIO_SFN(1));
+ s3c_gpio_cfgpin(OM_3D7K_GPIO_MODEM_ON, S3C_GPIO_SFN(1));
+ gpio_direction_output(OM_3D7K_GPIO_N_MODEM_RESET, 0);
+ gpio_direction_output(OM_3D7K_GPIO_MODEM_ON, 1);
+ msleep(10);
+ gpio_direction_output(OM_3D7K_GPIO_MODEM_ON, 0);
+ msleep(150);
+ gpio_direction_output(OM_3D7K_GPIO_N_MODEM_RESET, 1);
+ msleep(300);
+ gpio_direction_output(OM_3D7K_GPIO_MODEM_ON, 1);
+ break;
+ case OM_3D7K_USBHOST:
+ pcf50633_gpio_set(om_3d7k_pcf, PCF50633_GPO, 1);
+ break;
+ case OM_3D7K_VIB:
+ gpio_direction_output(OM_3D7K_GPIO_VIBRATOR_ON, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+static void om_3d7k_features_pwron_set_off(enum feature feature)
+{
+ int gpio;
+
+ switch (feature) {
+ case OM_3D7K_GPS:
+ /* disable LNA */
+ gpio_direction_output(OM_3D7K_GPIO_GPS_LNA_EN, 0);
+ regulator_disable(gps_regulator);
+ break;
+ case OM_3D7K_WLAN_BT:
+ gpio_direction_output(OM_3D7K_GPIO_WLAN_RESET, 0);
+ s3c_gpio_setpull(OM_3D7K_GPIO_WLAN_RESET, S3C_GPIO_PULL_NONE);
+ s3c_gpio_cfgpin(OM_3D7K_GPIO_WLAN_RESET, S3C_GPIO_SFN(1));
+
+ gpio_direction_output(OM_3D7K_GPIO_WLAN_PWRDN, 0);
+ s3c_gpio_setpull(OM_3D7K_GPIO_WLAN_PWRDN, S3C_GPIO_PULL_NONE);
+ s3c_gpio_cfgpin(OM_3D7K_GPIO_WLAN_PWRDN, S3C_GPIO_SFN(1));
+ msleep(500);
+ /* remove power from WLAN / BT module */
+ gpio_direction_output(OM_3D7K_GPIO_NWLAN_POWER, 1);
+ s3c_gpio_setpull(OM_3D7K_GPIO_NWLAN_POWER, S3C_GPIO_PULL_NONE);
+ s3c_gpio_cfgpin(OM_3D7K_GPIO_NWLAN_POWER, S3C_GPIO_SFN(1));
+
+ sdhci_s3c_force_presence_change(&s3c_device_hsmmc1);
+ for (gpio = S3C64XX_GPH(0); gpio < S3C64XX_GPH(6); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0)); /* input */
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_DOWN);
+ }
+ break;
+ case OM_3D7K_GSM:
+ /* remove power from WLAN / BT module */
+ s3c_gpio_cfgpin(OM_3D7K_GPIO_MODEM_ON, S3C_GPIO_SFN(1));
+ gpio_direction_output(OM_3D7K_GPIO_MODEM_ON, 0);
+ msleep(1100);
+ gpio_direction_output(OM_3D7K_GPIO_MODEM_ON, 1);
+ break;
+ case OM_3D7K_USBHOST:
+ pcf50633_gpio_set(om_3d7k_pcf, PCF50633_GPO, 0);
+ break;
+ case OM_3D7K_VIB:
+ gpio_direction_output(OM_3D7K_GPIO_VIBRATOR_ON, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static void om_3d7k_features_pwron_set(enum feature feature, int on)
+{
+ if ((on) && (!feature_info[feature].on))
+ om_3d7k_features_pwron_set_on(feature);
+ else
+ if ((!on) && (feature_info[feature].on))
+ om_3d7k_features_pwron_set_off(feature);
+}
+
+static ssize_t om_3d7k_feature_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int on;
+ int feature = 0;
+ int hit = 0;
+
+ while (!hit && feature < OM_3D7K_FEATURE_COUNT) {
+ if (!strcmp(attr->attr.name, feature_info[feature].name))
+ hit = 1;
+ else
+ feature++;
+ }
+
+ if (!hit)
+ return -EINVAL;
+
+ switch (feature) {
+ case OM_3D7K_GPS:
+ on = regulator_is_enabled(gps_regulator);
+ break;
+ case OM_3D7K_USBHOST:
+ on = pcf50633_gpio_get(om_3d7k_pcf, PCF50633_GPO);
+ break;
+ default:
+ on = feature_info[feature].on;
+ }
+
+ *buf++ = '0' + on;
+ *buf++='\n';
+ *buf = '\0';
+
+ return 3;
+}
+
+static ssize_t om_3d7k_feature_write(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int on = !!simple_strtoul(buf, NULL, 10);
+ int feature = 0;
+ int hit = 0;
+
+ while (!hit && feature < OM_3D7K_FEATURE_COUNT) {
+ if (!strcmp(attr->attr.name, feature_info[feature].name))
+ hit = 1;
+ else
+ feature++;
+ }
+
+ if (!hit)
+ return -EINVAL;
+
+ om_3d7k_features_pwron_set(feature, on);
+ feature_info[feature].on = on;
+
+ return count;
+}
+
+
+static DEVICE_ATTR(gps_power, 0644, om_3d7k_feature_read,
+ om_3d7k_feature_write);
+
+static DEVICE_ATTR(wlan_bt_power, 0644, om_3d7k_feature_read,
+ om_3d7k_feature_write);
+
+static DEVICE_ATTR(gsm_power, 0644, om_3d7k_feature_read,
+ om_3d7k_feature_write);
+
+static DEVICE_ATTR(usbhost_power, 0644, om_3d7k_feature_read,
+ om_3d7k_feature_write);
+
+static DEVICE_ATTR(vibrator_power, 0644, om_3d7k_feature_read,
+ om_3d7k_feature_write);
+
+
+static struct attribute *om_3d7k_features_sysfs_entries[] = {
+ &dev_attr_gps_power.attr,
+ &dev_attr_wlan_bt_power.attr,
+ &dev_attr_gsm_power.attr,
+ &dev_attr_usbhost_power.attr,
+ &dev_attr_vibrator_power.attr,
+ NULL
+};
+
+
+static struct attribute_group om_3d7k_features_attr_group = {
+ .name = NULL,
+ .attrs = om_3d7k_features_sysfs_entries,
+};
+
+static int __init om_3d7k_features_probe(struct platform_device *pdev)
+{
+ gps_regulator = regulator_get(&pdev->dev, "RF_3V");
+ dev_info(&pdev->dev, "starting\n");
+
+ return sysfs_create_group(&pdev->dev.kobj,
+ &om_3d7k_features_attr_group);
+}
+
+static int om_3d7k_features_remove(struct platform_device *pdev)
+{
+
+ regulator_put(gps_regulator);
+ sysfs_remove_group(&pdev->dev.kobj, &om_3d7k_features_attr_group);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int om_3d7k_features_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ int feature;
+
+ for (feature = 0; feature < OM_3D7K_FEATURE_COUNT; feature++)
+ if (feature_info[feature].depower_on_suspend)
+ om_3d7k_features_pwron_set_off(feature);
+
+ return 0;
+}
+
+static int om_3d7k_features_resume(struct platform_device *pdev)
+{
+ int feature;
+
+ for (feature = 0; feature < OM_3D7K_FEATURE_COUNT; feature++)
+ if (feature_info[feature].depower_on_suspend)
+ if (feature_info[feature].on)
+ om_3d7k_features_pwron_set_on(feature);
+
+ return 0;
+}
+#else
+#define om_3d7k_features_suspend NULL
+#define om_3d7k_features_resume NULL
+#endif
+
+static struct platform_driver om_3d7k_features_driver = {
+ .probe = om_3d7k_features_probe,
+ .remove = om_3d7k_features_remove,
+ .suspend = om_3d7k_features_suspend,
+ .resume = om_3d7k_features_resume,
+ .driver = {
+ .name = "om-3d7k",
+ },
+};
+
+static int __devinit om_3d7k_features_init(void)
+{
+ return platform_driver_register(&om_3d7k_features_driver);
+}
+
+static void om_3d7k_features_exit(void)
+{
+ platform_driver_unregister(&om_3d7k_features_driver);
+}
+
+module_init(om_3d7k_features_init);
+module_exit(om_3d7k_features_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andy Green <andy@openmoko.com>");
+MODULE_DESCRIPTION("Openmoko OM_3D7K Feature Driver");