summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeomho Seo <beomho.seo@samsung.com>2014-05-15 12:33:33 +0900
committerBeomho Seo <beomho.seo@samsung.com>2014-05-15 12:33:33 +0900
commit63d6bbe3d13609144e46d6245940ae11ddfb629b (patch)
tree86b07de9769844214d9516593671559b94b98edf
parent87f73e89289e9115df906ce671c19f9054a99cac (diff)
downloadlinux-3.10-63d6bbe3d13609144e46d6245940ae11ddfb629b.tar.gz
linux-3.10-63d6bbe3d13609144e46d6245940ae11ddfb629b.tar.bz2
linux-3.10-63d6bbe3d13609144e46d6245940ae11ddfb629b.zip
WORKAROUND: net: rfkill-gpio: add host wake and wake pins
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 0a5e2a3b503..a995b2af577 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 4d09f6eab35..64624ba6f49 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 212a77f38f2..37f9dc8f7a2 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),
},
};