pwm: Add table-based lookup for static mappings
authorThierry Reding <thierry.reding@avionic-design.de>
Mon, 26 Mar 2012 06:42:48 +0000 (08:42 +0200)
committerThierry Reding <thierry.reding@avionic-design.de>
Fri, 15 Jun 2012 10:56:53 +0000 (12:56 +0200)
In order to get rid of the global namespace for PWM devices, this commit
provides an alternative method, similar to that of the regulator or
clock frameworks, for registering a static mapping for PWM devices. This
works by providing a table with a provider/consumer map in the board
setup code.

With the new pwm_get() and pwm_put() functions available, usage of
pwm_request() and pwm_free() becomes deprecated.

Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Documentation/pwm.txt
drivers/pwm/core.c
include/linux/pwm.h

index 48f598a..554290e 100644 (file)
@@ -12,14 +12,33 @@ this kind of flexibility the generic PWM API exists.
 Identifying PWMs
 ----------------
 
-Users of the legacy PWM API use unique IDs to refer to PWM devices. One
-goal of the new PWM framework is to get rid of this global namespace.
+Users of the legacy PWM API use unique IDs to refer to PWM devices.
+
+Instead of referring to a PWM device via its unique ID, board setup code
+should instead register a static mapping that can be used to match PWM
+consumers to providers, as given in the following example:
+
+       static struct pwm_lookup board_pwm_lookup[] = {
+               PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL),
+       };
+
+       static void __init board_init(void)
+       {
+               ...
+               pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup));
+               ...
+       }
 
 Using PWMs
 ----------
 
-A PWM can be requested using pwm_request() and freed after usage with
-pwm_free(). After being requested a PWM has to be configured using
+Legacy users can request a PWM device using pwm_request() and free it
+after usage with pwm_free().
+
+New users should use the pwm_get() function and pass to it the consumer
+device or a consumer name. pwm_put() is used to free the PWM device.
+
+After being requested a PWM has to be configured using:
 
 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
 
index aadc1d7..a2af599 100644 (file)
@@ -32,6 +32,8 @@
 
 #define MAX_PWMS 1024
 
+static DEFINE_MUTEX(pwm_lookup_lock);
+static LIST_HEAD(pwm_lookup_list);
 static DEFINE_MUTEX(pwm_lock);
 static LIST_HEAD(pwm_chips);
 static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
@@ -80,6 +82,29 @@ static void free_pwms(struct pwm_chip *chip)
        chip->pwms = NULL;
 }
 
+static struct pwm_chip *pwmchip_find_by_name(const char *name)
+{
+       struct pwm_chip *chip;
+
+       if (!name)
+               return NULL;
+
+       mutex_lock(&pwm_lock);
+
+       list_for_each_entry(chip, &pwm_chips, list) {
+               const char *chip_name = dev_name(chip->dev);
+
+               if (chip_name && strcmp(chip_name, name) == 0) {
+                       mutex_unlock(&pwm_lock);
+                       return chip;
+               }
+       }
+
+       mutex_unlock(&pwm_lock);
+
+       return NULL;
+}
+
 static int pwm_device_request(struct pwm_device *pwm, const char *label)
 {
        int err;
@@ -218,6 +243,8 @@ EXPORT_SYMBOL_GPL(pwmchip_remove);
  * pwm_request() - request a PWM device
  * @pwm_id: global PWM device index
  * @label: PWM device label
+ *
+ * This function is deprecated, use pwm_get() instead.
  */
 struct pwm_device *pwm_request(int pwm, const char *label)
 {
@@ -281,24 +308,12 @@ EXPORT_SYMBOL_GPL(pwm_request_from_chip);
 /**
  * pwm_free() - free a PWM device
  * @pwm: PWM device
+ *
+ * This function is deprecated, use pwm_put() instead.
  */
 void pwm_free(struct pwm_device *pwm)
 {
-       mutex_lock(&pwm_lock);
-
-       if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
-               pr_warning("PWM device already freed\n");
-               goto out;
-       }
-
-       if (pwm->chip->ops->free)
-               pwm->chip->ops->free(pwm->chip, pwm);
-
-       pwm->label = NULL;
-
-       module_put(pwm->chip->ops->owner);
-out:
-       mutex_unlock(&pwm_lock);
+       pwm_put(pwm);
 }
 EXPORT_SYMBOL_GPL(pwm_free);
 
@@ -341,6 +356,130 @@ void pwm_disable(struct pwm_device *pwm)
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
 
+/**
+ * pwm_add_table() - register PWM device consumers
+ * @table: array of consumers to register
+ * @num: number of consumers in table
+ */
+void __init pwm_add_table(struct pwm_lookup *table, size_t num)
+{
+       mutex_lock(&pwm_lookup_lock);
+
+       while (num--) {
+               list_add_tail(&table->list, &pwm_lookup_list);
+               table++;
+       }
+
+       mutex_unlock(&pwm_lookup_lock);
+}
+
+/**
+ * pwm_get() - look up and request a PWM device
+ * @dev: device for PWM consumer
+ * @con_id: consumer name
+ *
+ * Look up a PWM chip and a relative index via a table supplied by board setup
+ * code (see pwm_add_table()).
+ *
+ * Once a PWM chip has been found the specified PWM device will be requested
+ * and is ready to be used.
+ */
+struct pwm_device *pwm_get(struct device *dev, const char *con_id)
+{
+       struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
+       const char *dev_id = dev ? dev_name(dev): NULL;
+       struct pwm_chip *chip = NULL;
+       unsigned int best = 0;
+       struct pwm_lookup *p;
+       unsigned int index;
+       unsigned int match;
+
+       /*
+        * We look up the provider in the static table typically provided by
+        * board setup code. We first try to lookup the consumer device by
+        * name. If the consumer device was passed in as NULL or if no match
+        * was found, we try to find the consumer by directly looking it up
+        * by name.
+        *
+        * If a match is found, the provider PWM chip is looked up by name
+        * and a PWM device is requested using the PWM device per-chip index.
+        *
+        * The lookup algorithm was shamelessly taken from the clock
+        * framework:
+        *
+        * We do slightly fuzzy matching here:
+        *  An entry with a NULL ID is assumed to be a wildcard.
+        *  If an entry has a device ID, it must match
+        *  If an entry has a connection ID, it must match
+        * Then we take the most specific entry - with the following order
+        * of precedence: dev+con > dev only > con only.
+        */
+       mutex_lock(&pwm_lookup_lock);
+
+       list_for_each_entry(p, &pwm_lookup_list, list) {
+               match = 0;
+
+               if (p->dev_id) {
+                       if (!dev_id || strcmp(p->dev_id, dev_id))
+                               continue;
+
+                       match += 2;
+               }
+
+               if (p->con_id) {
+                       if (!con_id || strcmp(p->con_id, con_id))
+                               continue;
+
+                       match += 1;
+               }
+
+               if (match > best) {
+                       chip = pwmchip_find_by_name(p->provider);
+                       index = p->index;
+
+                       if (match != 3)
+                               best = match;
+                       else
+                               break;
+               }
+       }
+
+       if (chip)
+               pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id);
+
+       mutex_unlock(&pwm_lookup_lock);
+
+       return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_get);
+
+/**
+ * pwm_put() - release a PWM device
+ * @pwm: PWM device
+ */
+void pwm_put(struct pwm_device *pwm)
+{
+       if (!pwm)
+               return;
+
+       mutex_lock(&pwm_lock);
+
+       if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
+               pr_warning("PWM device already freed\n");
+               goto out;
+       }
+
+       if (pwm->chip->ops->free)
+               pwm->chip->ops->free(pwm->chip, pwm);
+
+       pwm->label = NULL;
+
+       module_put(pwm->chip->ops->owner);
+out:
+       mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL_GPL(pwm_put);
+
 #ifdef CONFIG_DEBUG_FS
 static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
 {
index 047cd53..2947a4f 100644 (file)
@@ -115,6 +115,28 @@ int pwmchip_remove(struct pwm_chip *chip);
 struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
                                         unsigned int index,
                                         const char *label);
+
+struct pwm_device *pwm_get(struct device *dev, const char *consumer);
+void pwm_put(struct pwm_device *pwm);
+
+struct pwm_lookup {
+       struct list_head list;
+       const char *provider;
+       unsigned int index;
+       const char *dev_id;
+       const char *con_id;
+};
+
+#define PWM_LOOKUP(_provider, _index, _dev_id, _con_id)        \
+       {                                               \
+               .provider = _provider,                  \
+               .index = _index,                        \
+               .dev_id = _dev_id,                      \
+               .con_id = _con_id,                      \
+       }
+
+void pwm_add_table(struct pwm_lookup *table, size_t num);
+
 #endif
 
 #endif /* __LINUX_PWM_H */