Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 319197
b: refs/heads/master
c: 8138d2d
h: refs/heads/master
i:
  319195: 01db6ed
v: v3
  • Loading branch information
Thierry Reding committed Jun 15, 2012
1 parent 3132d5e commit 5b53d39
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 20 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 62099abf67a20cfb98d4c031fb1925e10a78ee1b
refs/heads/master: 8138d2ddbcca2a100482dac390133f83c5a60f94
27 changes: 23 additions & 4 deletions trunk/Documentation/pwm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
169 changes: 154 additions & 15 deletions trunk/drivers/pwm/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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)
{
Expand Down
22 changes: 22 additions & 0 deletions trunk/include/linux/pwm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

0 comments on commit 5b53d39

Please sign in to comment.