summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeomho Seo <beomho.seo@samsung.com>2014-05-15 03:33:33 (GMT)
committerBeomho Seo <beomho.seo@samsung.com>2014-05-15 03:33:33 (GMT)
commit63d6bbe3d13609144e46d6245940ae11ddfb629b (patch)
tree86b07de9769844214d9516593671559b94b98edf
parent87f73e89289e9115df906ce671c19f9054a99cac (diff)
downloadlinux-3.10-63d6bbe3d13609144e46d6245940ae11ddfb629b.zip
linux-3.10-63d6bbe3d13609144e46d6245940ae11ddfb629b.tar.gz
linux-3.10-63d6bbe3d13609144e46d6245940ae11ddfb629b.tar.bz2
WORKAROUND: net: rfkill-gpio: add host wake and wake pinsrefs/changes/77/21077/1
WORKAROUND: Temporary workaround for bluetooth enable. The wake pin is used for waking up the hw and host wake pin is used for waking up host processor during sleep. Additionally, This patch add reset-gpio property and devm_gpio_request_one function. Change-Id: I3695fce3b5c6162c9bad5fa0721df9bd8df505c7 Signed-off-by: Beomho Seo <beomho.seo@samsung.com> Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com>
-rw-r--r--arch/arm/boot/dts/exynos4412-trats2.dts3
-rw-r--r--include/linux/rfkill-gpio.h4
-rw-r--r--net/rfkill/rfkill-gpio.c99
3 files changed, 106 insertions, 0 deletions
diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts
index 0a5e2a3..a995b2a 100644
--- a/arch/arm/boot/dts/exynos4412-trats2.dts
+++ b/arch/arm/boot/dts/exynos4412-trats2.dts
@@ -425,6 +425,9 @@
rfkill-name = "bt";
rfkill-type = <2>;
shutdown-gpio = <&gpl0 6 0>;
+ wake-gpio = <&gpx3 1 0>;
+ host-wake-gpio = <&gpx2 6 0>;
+ reset-gpio = <0>;
};
i2c_ak8975: i2c-gpio-0 {
diff --git a/include/linux/rfkill-gpio.h b/include/linux/rfkill-gpio.h
index 4d09f6e..64624ba 100644
--- a/include/linux/rfkill-gpio.h
+++ b/include/linux/rfkill-gpio.h
@@ -29,6 +29,8 @@
* @name: name for the gpio rf kill instance
* @reset_gpio: GPIO which is used for reseting rfkill switch
* @shutdown_gpio: GPIO which is used for shutdown of rfkill switch
+ * @wake_gpio: GPIO which is used for wake of rfkill switch
+ * @host_wake_gpio: GPIO which is used for wake of host from rfkill switch
* @power_clk_name: [optional] name of clk to turn off while blocked
* @gpio_runtime_close: clean up platform specific gpio configuration
* @gpio_runtime_setup: set up platform specific gpio configuration
@@ -38,6 +40,8 @@ struct rfkill_gpio_platform_data {
char *name;
int reset_gpio;
int shutdown_gpio;
+ int wake_gpio;
+ int host_wake_gpio;
const char *power_clk_name;
enum rfkill_type type;
void (*gpio_runtime_close)(struct platform_device *);
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index 212a77f..37f9dc8 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -27,19 +27,26 @@
#include <linux/of_gpio.h>
#include <linux/rfkill-gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
struct rfkill_gpio_data {
const char *name;
enum rfkill_type type;
int reset_gpio;
int shutdown_gpio;
+ int wake_gpio;
+ int host_wake_gpio;
struct rfkill *rfkill_dev;
char *reset_name;
char *shutdown_name;
+ char *wake_name;
+ char *host_wake_name;
struct clk *clk;
bool clk_enabled;
+ int irq;
};
static int rfkill_gpio_set_power(void *data, bool blocked)
@@ -51,11 +58,15 @@ static int rfkill_gpio_set_power(void *data, bool blocked)
gpio_set_value(rfkill->shutdown_gpio, 0);
if (gpio_is_valid(rfkill->reset_gpio))
gpio_set_value(rfkill->reset_gpio, 0);
+ if (gpio_is_valid(rfkill->wake_gpio))
+ gpio_set_value(rfkill->wake_gpio, 0);
if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled)
clk_disable_unprepare(rfkill->clk);
} else {
if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
clk_prepare_enable(rfkill->clk);
+ if (gpio_is_valid(rfkill->wake_gpio))
+ gpio_set_value(rfkill->wake_gpio, 1);
if (gpio_is_valid(rfkill->reset_gpio))
gpio_set_value(rfkill->reset_gpio, 1);
if (gpio_is_valid(rfkill->shutdown_gpio))
@@ -80,10 +91,28 @@ static int rfkill_gpio_dt_probe(struct device *dev,
of_property_read_string(np, "rfkill-name", &rfkill->name);
of_property_read_u32(np, "rfkill-type", &rfkill->type);
rfkill->shutdown_gpio = of_get_named_gpio(np, "shutdown-gpio", 0);
+ rfkill->wake_gpio = of_get_named_gpio(np, "wake-gpio", 0);
+ rfkill->host_wake_gpio = of_get_named_gpio(np, "host-wake-gpio", 0);
+ rfkill->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+
return 0;
}
+static irqreturn_t rfkill_gpio_irq_handler(int irq, void *data)
+{
+ struct rfkill_gpio_data *rfkill = data;
+ int host_wake;
+ unsigned int type;
+
+ host_wake = gpio_get_value(rfkill->host_wake_gpio);
+ type = (host_wake ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH) |
+ IRQF_NO_SUSPEND;
+ irq_set_irq_type(rfkill->irq, type);
+
+ return IRQ_HANDLED;
+}
+
static int rfkill_gpio_probe(struct platform_device *pdev)
{
struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
@@ -158,6 +187,39 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
}
}
+ if (gpio_is_valid(rfkill->wake_gpio)) {
+ ret = devm_gpio_request_one(&pdev->dev, rfkill->wake_gpio,
+ 0, rfkill->wake_name);
+ if (ret) {
+ pr_warn("%s: failed to get wake gpio.\n", __func__);
+ return ret;
+ }
+ }
+
+ if (gpio_is_valid(rfkill->host_wake_gpio)) {
+ ret = devm_gpio_request_one(&pdev->dev, rfkill->host_wake_gpio,
+ 1, rfkill->host_wake_name);
+ if (ret) {
+ pr_warn("%s: failed to get host wake gpio.\n",
+ __func__);
+ return ret;
+ }
+
+ rfkill->irq = gpio_to_irq(rfkill->host_wake_gpio);
+ ret = devm_request_irq(&pdev->dev, rfkill->irq,
+ rfkill_gpio_irq_handler,
+ IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND,
+ "rfkill-gpio-irq", rfkill);
+ if (ret) {
+ pr_warn("%s: failed to request IRQ(%d) ret(%d)\n",
+ __func__, rfkill->irq, ret);
+ return ret;
+ }
+ ret = irq_set_irq_wake(rfkill->irq, 1);
+ if (ret)
+ return ret;
+ }
+
rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev,
rfkill->type, &rfkill_gpio_ops,
rfkill);
@@ -174,6 +236,8 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
rfkill_set_sw_state(rfkill->rfkill_dev, true);
+ device_init_wakeup(&pdev->dev, true);
+
dev_info(&pdev->dev, "%s device registered.\n", rfkill->name);
return 0;
@@ -192,6 +256,40 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int rfkill_gpio_suspend(struct device *dev)
+{
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device, dev);
+ struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rfkill->irq);
+
+ return 0;
+}
+
+static int rfkill_gpio_resume(struct device *dev)
+{
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device, dev);
+ struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rfkill->irq);
+
+ return 0;
+}
+#else
+#define rfkill_gpio_suspend NULL
+#define rfkill_gpio_resume NULL
+#endif /* CONFIG_PM */
+
+const struct dev_pm_ops rfkill_gpio_pm = {
+ .suspend = rfkill_gpio_suspend,
+ .resume = rfkill_gpio_resume,
+};
+
static const struct of_device_id rfkill_of_match[] = {
{ .compatible = "rfkill-gpio", },
{},
@@ -203,6 +301,7 @@ static struct platform_driver rfkill_gpio_driver = {
.driver = {
.name = "rfkill_gpio",
.owner = THIS_MODULE,
+ .pm = &rfkill_gpio_pm,
.of_match_table = of_match_ptr(rfkill_of_match),
},
};