]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
greybus: svc watchdog: allow it to be enabled/disabled from userspace
authorGreg Kroah-Hartman <gregkh@google.com>
Tue, 26 Jan 2016 23:17:08 +0000 (15:17 -0800)
committerGreg Kroah-Hartman <gregkh@google.com>
Wed, 27 Jan 2016 01:06:29 +0000 (17:06 -0800)
Sometimes you want to disable the SVC watchdog without having to patch
your kernel, so provide a sysfs file to do this.  This can let us do
power measurements, and let the firmware developers not go crazy
worrying that the kernel is going to reset their chips with no notice.

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/Documentation/sysfs-bus-greybus
drivers/staging/greybus/svc.c
drivers/staging/greybus/svc.h
drivers/staging/greybus/svc_watchdog.c

index 1550b7f8d77673bc6cf3a31e8aba5b68bb7c00ce..4493605186b02b576e055a8795a0ff2644710e37 100644 (file)
@@ -163,3 +163,11 @@ KernelVersion:     4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                The version number of the firmware in the SVC device.
+
+What:          /sys/bus/greybus/device/N-svc/watchdog
+Date:          October 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               If the SVC watchdog is enabled or not.  Writing 0 to this
+               file will disable the watchdog, writing 1 will enable it.
index b9e5b856501024458c6a85dd5bc96d4671a2bca2..eeb251e2844113c5474032380a67213872be3466 100644 (file)
@@ -70,10 +70,42 @@ static ssize_t intf_eject_store(struct device *dev,
 }
 static DEVICE_ATTR_WO(intf_eject);
 
+static ssize_t watchdog_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct gb_svc *svc = to_gb_svc(dev);
+
+       return sprintf(buf, "%s\n",
+                      gb_svc_watchdog_enabled(svc) ? "enabled" : "disabled");
+}
+
+static ssize_t watchdog_store(struct device *dev,
+                             struct device_attribute *attr, const char *buf,
+                             size_t len)
+{
+       struct gb_svc *svc = to_gb_svc(dev);
+       int retval;
+       bool user_request;
+
+       retval = strtobool(buf, &user_request);
+       if (retval)
+               return retval;
+
+       if (user_request)
+               retval = gb_svc_watchdog_enable(svc);
+       else
+               retval = gb_svc_watchdog_disable(svc);
+       if (retval)
+               return retval;
+       return len;
+}
+static DEVICE_ATTR_RW(watchdog);
+
 static struct attribute *svc_attrs[] = {
        &dev_attr_endo_id.attr,
        &dev_attr_ap_intf_id.attr,
        &dev_attr_intf_eject.attr,
+       &dev_attr_watchdog.attr,
        NULL,
 };
 ATTRIBUTE_GROUPS(svc);
index 0f81e97a2e8867067182bc719f8c4d9e299dd508..b9fb93ea56beff63393fded20e451cdeae74ff7e 100644 (file)
@@ -61,6 +61,9 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series,
 int gb_svc_ping(struct gb_svc *svc);
 int gb_svc_watchdog_create(struct gb_svc *svc);
 void gb_svc_watchdog_destroy(struct gb_svc *svc);
+bool gb_svc_watchdog_enabled(struct gb_svc *svc);
+int gb_svc_watchdog_enable(struct gb_svc *svc);
+int gb_svc_watchdog_disable(struct gb_svc *svc);
 
 int gb_svc_protocol_init(void);
 void gb_svc_protocol_exit(void);
index edb73efd53f7d86a8d65ca292717844a13a079c1..9bd68f4f4cf7c138ef556fbaeca01480e9d446bc 100644 (file)
@@ -15,7 +15,7 @@
 struct gb_svc_watchdog {
        struct delayed_work     work;
        struct gb_svc           *svc;
-       bool                    finished;
+       bool                    enabled;
 };
 
 static struct delayed_work reset_work;
@@ -65,11 +65,16 @@ static void do_work(struct work_struct *work)
 
                INIT_DELAYED_WORK(&reset_work, greybus_reset);
                queue_delayed_work(system_wq, &reset_work, HZ/2);
-               return;
+
+               /*
+                * Disable ourselves, we don't want to trip again unless
+                * userspace wants us to.
+                */
+               watchdog->enabled = false;
        }
 
        /* resubmit our work to happen again, if we are still "alive" */
-       if (!watchdog->finished)
+       if (watchdog->enabled)
                queue_delayed_work(system_wq, &watchdog->work,
                                   SVC_WATCHDOG_PERIOD);
 }
@@ -85,14 +90,12 @@ int gb_svc_watchdog_create(struct gb_svc *svc)
        if (!watchdog)
                return -ENOMEM;
 
-       watchdog->finished = false;
+       watchdog->enabled = false;
        watchdog->svc = svc;
        INIT_DELAYED_WORK(&watchdog->work, do_work);
        svc->watchdog = watchdog;
 
-       queue_delayed_work(system_wq, &watchdog->work,
-                          SVC_WATCHDOG_PERIOD);
-       return 0;
+       return gb_svc_watchdog_enable(svc);
 }
 
 void gb_svc_watchdog_destroy(struct gb_svc *svc)
@@ -102,8 +105,47 @@ void gb_svc_watchdog_destroy(struct gb_svc *svc)
        if (!watchdog)
                return;
 
-       watchdog->finished = true;
-       cancel_delayed_work_sync(&watchdog->work);
+       gb_svc_watchdog_disable(svc);
        svc->watchdog = NULL;
        kfree(watchdog);
 }
+
+bool gb_svc_watchdog_enabled(struct gb_svc *svc)
+{
+       if (!svc || !svc->watchdog)
+               return false;
+       return svc->watchdog->enabled;
+}
+
+int gb_svc_watchdog_enable(struct gb_svc *svc)
+{
+       struct gb_svc_watchdog *watchdog;
+
+       if (!svc->watchdog)
+               return -ENODEV;
+
+       watchdog = svc->watchdog;
+       if (watchdog->enabled)
+               return 0;
+
+       watchdog->enabled = true;
+       queue_delayed_work(system_wq, &watchdog->work,
+                          SVC_WATCHDOG_PERIOD);
+       return 0;
+}
+
+int gb_svc_watchdog_disable(struct gb_svc *svc)
+{
+       struct gb_svc_watchdog *watchdog;
+
+       if (!svc->watchdog)
+               return -ENODEV;
+
+       watchdog = svc->watchdog;
+       if (!watchdog->enabled)
+               return 0;
+
+       watchdog->enabled = false;
+       cancel_delayed_work_sync(&watchdog->work);
+       return 0;
+}