soc/tegra: Changes for v5.2-rc1
Besides a couple of fixes to better cope with deferred probing, this set of patches also implements the acquire/release protocol for resets used during powergate operations. This is necessary to allow these resets to be temporarily shared with other devices that may also need to control these resets. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEiOrDCAFJzPfAjcif3SOs138+s6EFAly4jLYTHHRyZWRpbmdA bnZpZGlhLmNvbQAKCRDdI6zXfz6zoT3mEAC0+8j1/l8AyNGGvwN1wnbWzIcQHLRJ +TBVP676sgfrZwUlgs35qDDnquiQNo5AYKa0As2QMb1dWIqO1NGiPWTneckYphj6 1W1wVoKYoqfR56skyk5Kmt+RfqEkVvRF/Bd06VFKJe8La/ww1eaYXamkKaGNM/j5 izRWhisdGQkd31X4pUJOWkHD7i1WN9RS643Dy9JDn2uzj8mgCC3IlC63ScaaC8Fq 5DVIvNFvF79yUaSzCWGjskU1WsDzjK0qLtrFhAnVZOMUTfX/wbbBr4/hykLGwyvp aefFh97tTLCokIbgh8BecXXrcle44GgvG1b8pXJ6bKli8Ayf8TFsaAVuN87h+YSv wXW4fSlRldtBQbBl4ofj/jiDmqlt5mO17VAUN9MWLIui4G5GR4iAjR3SGqtK+SgD VdxSR1/Hk5MIAJeV6XuvDfdHoGkskVLDMiODcBqciXIZfPhOGnjz4s8EgMLGxs2F aQYpmGJdQDY0D+zt2PC+9CQvqX7iIFTWf5kywQTbMJ+dKTtOSeKl2zt85snnuuYI 668pXSm0EJl+oK/gpBMbl3N6qRIe9x46cJ54E9fqGG4PW6ivb0n1GzwcQcm6IY8e sUhLM0dQTOvevLUHrzCap5kPTTMT2Iji9SgW3S7hx0FvHtg6ni+pEkMekAyqid9H 1sRl96LBPaoj6Q== =27G2 -----END PGP SIGNATURE----- Merge tag 'tegra-for-5.2-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into arm/drivers soc/tegra: Changes for v5.2-rc1 Besides a couple of fixes to better cope with deferred probing, this set of patches also implements the acquire/release protocol for resets used during powergate operations. This is necessary to allow these resets to be temporarily shared with other devices that may also need to control these resets. * tag 'tegra-for-5.2-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux: soc/tegra: pmc: Move powergate initialisation to probe soc/tegra: pmc: Remove reset sysfs entries on error soc/tegra: pmc: Fix reset sources and levels soc/tegra: pmc: Implement acquire/release for resets reset: Add acquire/release support for arrays reset: Add acquired flag to of_reset_control_array_get() reset: add acquired/released state for exclusive reset controls Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
commit
990d4322cc
|
@ -34,6 +34,7 @@ static LIST_HEAD(reset_lookup_list);
|
|||
* @id: ID of the reset controller in the reset
|
||||
* controller device
|
||||
* @refcnt: Number of gets of this reset_control
|
||||
* @acquired: Only one reset_control may be acquired for a given rcdev and id.
|
||||
* @shared: Is this a shared (1), or an exclusive (0) reset_control?
|
||||
* @deassert_cnt: Number of times this reset line has been deasserted
|
||||
* @triggered_count: Number of times this reset line has been reset. Currently
|
||||
|
@ -45,6 +46,7 @@ struct reset_control {
|
|||
struct list_head list;
|
||||
unsigned int id;
|
||||
struct kref refcnt;
|
||||
bool acquired;
|
||||
bool shared;
|
||||
bool array;
|
||||
atomic_t deassert_count;
|
||||
|
@ -63,6 +65,17 @@ struct reset_control_array {
|
|||
struct reset_control *rstc[];
|
||||
};
|
||||
|
||||
static const char *rcdev_name(struct reset_controller_dev *rcdev)
|
||||
{
|
||||
if (rcdev->dev)
|
||||
return dev_name(rcdev->dev);
|
||||
|
||||
if (rcdev->of_node)
|
||||
return rcdev->of_node->full_name;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_reset_simple_xlate - translate reset_spec to the reset line number
|
||||
* @rcdev: a pointer to the reset controller device
|
||||
|
@ -232,6 +245,34 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int reset_control_array_acquire(struct reset_control_array *resets)
|
||||
{
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < resets->num_rstcs; i++) {
|
||||
err = reset_control_acquire(resets->rstc[i]);
|
||||
if (err < 0)
|
||||
goto release;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
release:
|
||||
while (i--)
|
||||
reset_control_release(resets->rstc[i]);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void reset_control_array_release(struct reset_control_array *resets)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < resets->num_rstcs; i++)
|
||||
reset_control_release(resets->rstc[i]);
|
||||
}
|
||||
|
||||
static inline bool reset_control_is_array(struct reset_control *rstc)
|
||||
{
|
||||
return rstc->array;
|
||||
|
@ -272,6 +313,9 @@ int reset_control_reset(struct reset_control *rstc)
|
|||
|
||||
if (atomic_inc_return(&rstc->triggered_count) != 1)
|
||||
return 0;
|
||||
} else {
|
||||
if (!rstc->acquired)
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
ret = rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
|
||||
|
@ -334,6 +378,12 @@ int reset_control_assert(struct reset_control *rstc)
|
|||
*/
|
||||
if (!rstc->rcdev->ops->assert)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (!rstc->acquired) {
|
||||
WARN(1, "reset %s (ID: %u) is not acquired\n",
|
||||
rcdev_name(rstc->rcdev), rstc->id);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id);
|
||||
|
@ -369,6 +419,12 @@ int reset_control_deassert(struct reset_control *rstc)
|
|||
|
||||
if (atomic_inc_return(&rstc->deassert_count) != 1)
|
||||
return 0;
|
||||
} else {
|
||||
if (!rstc->acquired) {
|
||||
WARN(1, "reset %s (ID: %u) is not acquired\n",
|
||||
rcdev_name(rstc->rcdev), rstc->id);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -406,9 +462,87 @@ int reset_control_status(struct reset_control *rstc)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(reset_control_status);
|
||||
|
||||
/**
|
||||
* reset_control_acquire() - acquires a reset control for exclusive use
|
||||
* @rstc: reset control
|
||||
*
|
||||
* This is used to explicitly acquire a reset control for exclusive use. Note
|
||||
* that exclusive resets are requested as acquired by default. In order for a
|
||||
* second consumer to be able to control the reset, the first consumer has to
|
||||
* release it first. Typically the easiest way to achieve this is to call the
|
||||
* reset_control_get_exclusive_released() to obtain an instance of the reset
|
||||
* control. Such reset controls are not acquired by default.
|
||||
*
|
||||
* Consumers implementing shared access to an exclusive reset need to follow
|
||||
* a specific protocol in order to work together. Before consumers can change
|
||||
* a reset they must acquire exclusive access using reset_control_acquire().
|
||||
* After they are done operating the reset, they must release exclusive access
|
||||
* with a call to reset_control_release(). Consumers are not granted exclusive
|
||||
* access to the reset as long as another consumer hasn't released a reset.
|
||||
*
|
||||
* See also: reset_control_release()
|
||||
*/
|
||||
int reset_control_acquire(struct reset_control *rstc)
|
||||
{
|
||||
struct reset_control *rc;
|
||||
|
||||
if (!rstc)
|
||||
return 0;
|
||||
|
||||
if (WARN_ON(IS_ERR(rstc)))
|
||||
return -EINVAL;
|
||||
|
||||
if (reset_control_is_array(rstc))
|
||||
return reset_control_array_acquire(rstc_to_array(rstc));
|
||||
|
||||
mutex_lock(&reset_list_mutex);
|
||||
|
||||
if (rstc->acquired) {
|
||||
mutex_unlock(&reset_list_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_for_each_entry(rc, &rstc->rcdev->reset_control_head, list) {
|
||||
if (rstc != rc && rstc->id == rc->id) {
|
||||
if (rc->acquired) {
|
||||
mutex_unlock(&reset_list_mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rstc->acquired = true;
|
||||
|
||||
mutex_unlock(&reset_list_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(reset_control_acquire);
|
||||
|
||||
/**
|
||||
* reset_control_release() - releases exclusive access to a reset control
|
||||
* @rstc: reset control
|
||||
*
|
||||
* Releases exclusive access right to a reset control previously obtained by a
|
||||
* call to reset_control_acquire(). Until a consumer calls this function, no
|
||||
* other consumers will be granted exclusive access.
|
||||
*
|
||||
* See also: reset_control_acquire()
|
||||
*/
|
||||
void reset_control_release(struct reset_control *rstc)
|
||||
{
|
||||
if (!rstc || WARN_ON(IS_ERR(rstc)))
|
||||
return;
|
||||
|
||||
if (reset_control_is_array(rstc))
|
||||
reset_control_array_release(rstc_to_array(rstc));
|
||||
else
|
||||
rstc->acquired = false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(reset_control_release);
|
||||
|
||||
static struct reset_control *__reset_control_get_internal(
|
||||
struct reset_controller_dev *rcdev,
|
||||
unsigned int index, bool shared)
|
||||
unsigned int index, bool shared, bool acquired)
|
||||
{
|
||||
struct reset_control *rstc;
|
||||
|
||||
|
@ -416,6 +550,14 @@ static struct reset_control *__reset_control_get_internal(
|
|||
|
||||
list_for_each_entry(rstc, &rcdev->reset_control_head, list) {
|
||||
if (rstc->id == index) {
|
||||
/*
|
||||
* Allow creating a secondary exclusive reset_control
|
||||
* that is initially not acquired for an already
|
||||
* controlled reset line.
|
||||
*/
|
||||
if (!rstc->shared && !shared && !acquired)
|
||||
break;
|
||||
|
||||
if (WARN_ON(!rstc->shared || !shared))
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
|
@ -434,6 +576,7 @@ static struct reset_control *__reset_control_get_internal(
|
|||
list_add(&rstc->list, &rcdev->reset_control_head);
|
||||
rstc->id = index;
|
||||
kref_init(&rstc->refcnt);
|
||||
rstc->acquired = acquired;
|
||||
rstc->shared = shared;
|
||||
|
||||
return rstc;
|
||||
|
@ -461,7 +604,7 @@ static void __reset_control_put_internal(struct reset_control *rstc)
|
|||
|
||||
struct reset_control *__of_reset_control_get(struct device_node *node,
|
||||
const char *id, int index, bool shared,
|
||||
bool optional)
|
||||
bool optional, bool acquired)
|
||||
{
|
||||
struct reset_control *rstc;
|
||||
struct reset_controller_dev *r, *rcdev;
|
||||
|
@ -514,7 +657,7 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
|
|||
}
|
||||
|
||||
/* reset_list_mutex also protects the rcdev's reset_control list */
|
||||
rstc = __reset_control_get_internal(rcdev, rstc_id, shared);
|
||||
rstc = __reset_control_get_internal(rcdev, rstc_id, shared, acquired);
|
||||
|
||||
out:
|
||||
mutex_unlock(&reset_list_mutex);
|
||||
|
@ -544,7 +687,7 @@ __reset_controller_by_name(const char *name)
|
|||
|
||||
static struct reset_control *
|
||||
__reset_control_get_from_lookup(struct device *dev, const char *con_id,
|
||||
bool shared, bool optional)
|
||||
bool shared, bool optional, bool acquired)
|
||||
{
|
||||
const struct reset_control_lookup *lookup;
|
||||
struct reset_controller_dev *rcdev;
|
||||
|
@ -574,7 +717,7 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id,
|
|||
|
||||
rstc = __reset_control_get_internal(rcdev,
|
||||
lookup->index,
|
||||
shared);
|
||||
shared, acquired);
|
||||
mutex_unlock(&reset_list_mutex);
|
||||
break;
|
||||
}
|
||||
|
@ -589,13 +732,18 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id,
|
|||
}
|
||||
|
||||
struct reset_control *__reset_control_get(struct device *dev, const char *id,
|
||||
int index, bool shared, bool optional)
|
||||
int index, bool shared, bool optional,
|
||||
bool acquired)
|
||||
{
|
||||
if (WARN_ON(shared && acquired))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (dev->of_node)
|
||||
return __of_reset_control_get(dev->of_node, id, index, shared,
|
||||
optional);
|
||||
optional, acquired);
|
||||
|
||||
return __reset_control_get_from_lookup(dev, id, shared, optional);
|
||||
return __reset_control_get_from_lookup(dev, id, shared, optional,
|
||||
acquired);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__reset_control_get);
|
||||
|
||||
|
@ -636,7 +784,7 @@ static void devm_reset_control_release(struct device *dev, void *res)
|
|||
|
||||
struct reset_control *__devm_reset_control_get(struct device *dev,
|
||||
const char *id, int index, bool shared,
|
||||
bool optional)
|
||||
bool optional, bool acquired)
|
||||
{
|
||||
struct reset_control **ptr, *rstc;
|
||||
|
||||
|
@ -645,7 +793,7 @@ struct reset_control *__devm_reset_control_get(struct device *dev,
|
|||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
rstc = __reset_control_get(dev, id, index, shared, optional);
|
||||
rstc = __reset_control_get(dev, id, index, shared, optional, acquired);
|
||||
if (!IS_ERR(rstc)) {
|
||||
*ptr = rstc;
|
||||
devres_add(dev, ptr);
|
||||
|
@ -672,7 +820,7 @@ int __device_reset(struct device *dev, bool optional)
|
|||
struct reset_control *rstc;
|
||||
int ret;
|
||||
|
||||
rstc = __reset_control_get(dev, NULL, 0, 0, optional);
|
||||
rstc = __reset_control_get(dev, NULL, 0, 0, optional, true);
|
||||
if (IS_ERR(rstc))
|
||||
return PTR_ERR(rstc);
|
||||
|
||||
|
@ -716,12 +864,15 @@ static int of_reset_control_get_count(struct device_node *node)
|
|||
* @np: device node for the device that requests the reset controls array
|
||||
* @shared: whether reset controls are shared or not
|
||||
* @optional: whether it is optional to get the reset controls
|
||||
* @acquired: only one reset control may be acquired for a given controller
|
||||
* and ID
|
||||
*
|
||||
* Returns pointer to allocated reset_control_array on success or
|
||||
* error on failure
|
||||
*/
|
||||
struct reset_control *
|
||||
of_reset_control_array_get(struct device_node *np, bool shared, bool optional)
|
||||
of_reset_control_array_get(struct device_node *np, bool shared, bool optional,
|
||||
bool acquired)
|
||||
{
|
||||
struct reset_control_array *resets;
|
||||
struct reset_control *rstc;
|
||||
|
@ -736,7 +887,8 @@ of_reset_control_array_get(struct device_node *np, bool shared, bool optional)
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
rstc = __of_reset_control_get(np, NULL, i, shared, optional);
|
||||
rstc = __of_reset_control_get(np, NULL, i, shared, optional,
|
||||
acquired);
|
||||
if (IS_ERR(rstc))
|
||||
goto err_rst;
|
||||
resets->rstc[i] = rstc;
|
||||
|
@ -783,7 +935,7 @@ devm_reset_control_array_get(struct device *dev, bool shared, bool optional)
|
|||
if (!devres)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
rstc = of_reset_control_array_get(dev->of_node, shared, optional);
|
||||
rstc = of_reset_control_array_get(dev->of_node, shared, optional, true);
|
||||
if (IS_ERR(rstc)) {
|
||||
devres_free(devres);
|
||||
return rstc;
|
||||
|
|
|
@ -268,6 +268,14 @@ static const char * const tegra186_reset_levels[] = {
|
|||
};
|
||||
|
||||
static const char * const tegra30_reset_sources[] = {
|
||||
"POWER_ON_RESET",
|
||||
"WATCHDOG",
|
||||
"SENSOR",
|
||||
"SW_MAIN",
|
||||
"LP0"
|
||||
};
|
||||
|
||||
static const char * const tegra210_reset_sources[] = {
|
||||
"POWER_ON_RESET",
|
||||
"WATCHDOG",
|
||||
"SENSOR",
|
||||
|
@ -656,10 +664,15 @@ static int tegra_genpd_power_on(struct generic_pm_domain *domain)
|
|||
int err;
|
||||
|
||||
err = tegra_powergate_power_up(pg, true);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_err(dev, "failed to turn on PM domain %s: %d\n",
|
||||
pg->genpd.name, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
reset_control_release(pg->reset);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -669,10 +682,18 @@ static int tegra_genpd_power_off(struct generic_pm_domain *domain)
|
|||
struct device *dev = pg->pmc->dev;
|
||||
int err;
|
||||
|
||||
err = reset_control_acquire(pg->reset);
|
||||
if (err < 0) {
|
||||
pr_err("failed to acquire resets: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = tegra_powergate_power_down(pg);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_err(dev, "failed to turn off PM domain %s: %d\n",
|
||||
pg->genpd.name, err);
|
||||
reset_control_release(pg->reset);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -937,38 +958,53 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
|
|||
struct device *dev = pg->pmc->dev;
|
||||
int err;
|
||||
|
||||
pg->reset = of_reset_control_array_get_exclusive(np);
|
||||
pg->reset = of_reset_control_array_get_exclusive_released(np);
|
||||
if (IS_ERR(pg->reset)) {
|
||||
err = PTR_ERR(pg->reset);
|
||||
dev_err(dev, "failed to get device resets: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (off)
|
||||
err = reset_control_assert(pg->reset);
|
||||
else
|
||||
err = reset_control_deassert(pg->reset);
|
||||
err = reset_control_acquire(pg->reset);
|
||||
if (err < 0) {
|
||||
pr_err("failed to acquire resets: %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (err)
|
||||
if (off) {
|
||||
err = reset_control_assert(pg->reset);
|
||||
} else {
|
||||
err = reset_control_deassert(pg->reset);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
reset_control_release(pg->reset);
|
||||
}
|
||||
|
||||
out:
|
||||
if (err) {
|
||||
reset_control_release(pg->reset);
|
||||
reset_control_put(pg->reset);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
|
||||
static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
|
||||
{
|
||||
struct device *dev = pmc->dev;
|
||||
struct tegra_powergate *pg;
|
||||
int id, err;
|
||||
int id, err = 0;
|
||||
bool off;
|
||||
|
||||
pg = kzalloc(sizeof(*pg), GFP_KERNEL);
|
||||
if (!pg)
|
||||
return;
|
||||
return -ENOMEM;
|
||||
|
||||
id = tegra_powergate_lookup(pmc, np->name);
|
||||
if (id < 0) {
|
||||
dev_err(dev, "powergate lookup failed for %pOFn: %d\n", np, id);
|
||||
err = -ENODEV;
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
|
@ -1021,7 +1057,7 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
|
|||
|
||||
dev_dbg(dev, "added PM domain %s\n", pg->genpd.name);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
|
||||
remove_genpd:
|
||||
pm_genpd_remove(&pg->genpd);
|
||||
|
@ -1040,25 +1076,67 @@ set_available:
|
|||
|
||||
free_mem:
|
||||
kfree(pg);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void tegra_powergate_init(struct tegra_pmc *pmc,
|
||||
struct device_node *parent)
|
||||
static int tegra_powergate_init(struct tegra_pmc *pmc,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct device_node *np, *child;
|
||||
unsigned int i;
|
||||
int err = 0;
|
||||
|
||||
/* Create a bitmap of the available and valid partitions */
|
||||
for (i = 0; i < pmc->soc->num_powergates; i++)
|
||||
if (pmc->soc->powergates[i])
|
||||
set_bit(i, pmc->powergates_available);
|
||||
np = of_get_child_by_name(parent, "powergates");
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
err = tegra_powergate_add(pmc, child);
|
||||
if (err < 0) {
|
||||
of_node_put(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void tegra_powergate_remove(struct generic_pm_domain *genpd)
|
||||
{
|
||||
struct tegra_powergate *pg = to_powergate(genpd);
|
||||
|
||||
reset_control_put(pg->reset);
|
||||
|
||||
while (pg->num_clks--)
|
||||
clk_put(pg->clks[pg->num_clks]);
|
||||
|
||||
kfree(pg->clks);
|
||||
|
||||
set_bit(pg->id, pmc->powergates_available);
|
||||
|
||||
kfree(pg);
|
||||
}
|
||||
|
||||
static void tegra_powergate_remove_all(struct device_node *parent)
|
||||
{
|
||||
struct generic_pm_domain *genpd;
|
||||
struct device_node *np, *child;
|
||||
|
||||
np = of_get_child_by_name(parent, "powergates");
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
for_each_child_of_node(np, child)
|
||||
tegra_powergate_add(pmc, child);
|
||||
for_each_child_of_node(np, child) {
|
||||
of_genpd_del_provider(child);
|
||||
|
||||
genpd = of_genpd_remove_last(child);
|
||||
if (IS_ERR(genpd))
|
||||
continue;
|
||||
|
||||
tegra_powergate_remove(genpd);
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
}
|
||||
|
@ -1709,13 +1787,16 @@ static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc)
|
|||
static ssize_t reset_reason_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
u32 value, rst_src;
|
||||
u32 value;
|
||||
|
||||
value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status);
|
||||
rst_src = (value & pmc->soc->regs->rst_source_mask) >>
|
||||
pmc->soc->regs->rst_source_shift;
|
||||
value &= pmc->soc->regs->rst_source_mask;
|
||||
value >>= pmc->soc->regs->rst_source_shift;
|
||||
|
||||
return sprintf(buf, "%s\n", pmc->soc->reset_sources[rst_src]);
|
||||
if (WARN_ON(value >= pmc->soc->num_reset_sources))
|
||||
return sprintf(buf, "%s\n", "UNKNOWN");
|
||||
|
||||
return sprintf(buf, "%s\n", pmc->soc->reset_sources[value]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(reset_reason);
|
||||
|
@ -1723,13 +1804,16 @@ static DEVICE_ATTR_RO(reset_reason);
|
|||
static ssize_t reset_level_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
u32 value, rst_lvl;
|
||||
u32 value;
|
||||
|
||||
value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status);
|
||||
rst_lvl = (value & pmc->soc->regs->rst_level_mask) >>
|
||||
pmc->soc->regs->rst_level_shift;
|
||||
value &= pmc->soc->regs->rst_level_mask;
|
||||
value >>= pmc->soc->regs->rst_level_shift;
|
||||
|
||||
return sprintf(buf, "%s\n", pmc->soc->reset_levels[rst_lvl]);
|
||||
if (WARN_ON(value >= pmc->soc->num_reset_levels))
|
||||
return sprintf(buf, "%s\n", "UNKNOWN");
|
||||
|
||||
return sprintf(buf, "%s\n", pmc->soc->reset_levels[value]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(reset_level);
|
||||
|
@ -1999,7 +2083,7 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
|
||||
err = tegra_powergate_debugfs_init();
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto cleanup_sysfs;
|
||||
}
|
||||
|
||||
err = register_restart_handler(&tegra_pmc_restart_handler);
|
||||
|
@ -2013,9 +2097,13 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto cleanup_restart_handler;
|
||||
|
||||
err = tegra_powergate_init(pmc, pdev->dev.of_node);
|
||||
if (err < 0)
|
||||
goto cleanup_powergates;
|
||||
|
||||
err = tegra_pmc_irq_init(pmc);
|
||||
if (err < 0)
|
||||
goto cleanup_restart_handler;
|
||||
goto cleanup_powergates;
|
||||
|
||||
mutex_lock(&pmc->powergates_lock);
|
||||
iounmap(pmc->base);
|
||||
|
@ -2026,10 +2114,15 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
cleanup_powergates:
|
||||
tegra_powergate_remove_all(pdev->dev.of_node);
|
||||
cleanup_restart_handler:
|
||||
unregister_restart_handler(&tegra_pmc_restart_handler);
|
||||
cleanup_debugfs:
|
||||
debugfs_remove(pmc->debugfs);
|
||||
cleanup_sysfs:
|
||||
device_remove_file(&pdev->dev, &dev_attr_reset_reason);
|
||||
device_remove_file(&pdev->dev, &dev_attr_reset_level);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -2185,7 +2278,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
|
|||
.init = tegra20_pmc_init,
|
||||
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
|
||||
.reset_sources = tegra30_reset_sources,
|
||||
.num_reset_sources = 5,
|
||||
.num_reset_sources = ARRAY_SIZE(tegra30_reset_sources),
|
||||
.reset_levels = NULL,
|
||||
.num_reset_levels = 0,
|
||||
};
|
||||
|
@ -2236,7 +2329,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
|
|||
.init = tegra20_pmc_init,
|
||||
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
|
||||
.reset_sources = tegra30_reset_sources,
|
||||
.num_reset_sources = 5,
|
||||
.num_reset_sources = ARRAY_SIZE(tegra30_reset_sources),
|
||||
.reset_levels = NULL,
|
||||
.num_reset_levels = 0,
|
||||
};
|
||||
|
@ -2347,7 +2440,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = {
|
|||
.init = tegra20_pmc_init,
|
||||
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
|
||||
.reset_sources = tegra30_reset_sources,
|
||||
.num_reset_sources = 5,
|
||||
.num_reset_sources = ARRAY_SIZE(tegra30_reset_sources),
|
||||
.reset_levels = NULL,
|
||||
.num_reset_levels = 0,
|
||||
};
|
||||
|
@ -2452,8 +2545,8 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
|
|||
.regs = &tegra20_pmc_regs,
|
||||
.init = tegra20_pmc_init,
|
||||
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
|
||||
.reset_sources = tegra30_reset_sources,
|
||||
.num_reset_sources = 5,
|
||||
.reset_sources = tegra210_reset_sources,
|
||||
.num_reset_sources = ARRAY_SIZE(tegra210_reset_sources),
|
||||
.reset_levels = NULL,
|
||||
.num_reset_levels = 0,
|
||||
};
|
||||
|
@ -2578,9 +2671,9 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
|
|||
.init = NULL,
|
||||
.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
|
||||
.reset_sources = tegra186_reset_sources,
|
||||
.num_reset_sources = 14,
|
||||
.num_reset_sources = ARRAY_SIZE(tegra186_reset_sources),
|
||||
.reset_levels = tegra186_reset_levels,
|
||||
.num_reset_levels = 3,
|
||||
.num_reset_levels = ARRAY_SIZE(tegra186_reset_levels),
|
||||
.num_wake_events = ARRAY_SIZE(tegra186_wake_events),
|
||||
.wake_events = tegra186_wake_events,
|
||||
};
|
||||
|
@ -2719,6 +2812,7 @@ static int __init tegra_pmc_early_init(void)
|
|||
const struct of_device_id *match;
|
||||
struct device_node *np;
|
||||
struct resource regs;
|
||||
unsigned int i;
|
||||
bool invert;
|
||||
|
||||
mutex_init(&pmc->powergates_lock);
|
||||
|
@ -2775,7 +2869,10 @@ static int __init tegra_pmc_early_init(void)
|
|||
if (pmc->soc->maybe_tz_only)
|
||||
pmc->tz_only = tegra_pmc_detect_tz_only(pmc);
|
||||
|
||||
tegra_powergate_init(pmc, np);
|
||||
/* Create a bitmap of the available and valid partitions */
|
||||
for (i = 0; i < pmc->soc->num_powergates; i++)
|
||||
if (pmc->soc->powergates[i])
|
||||
set_bit(i, pmc->powergates_available);
|
||||
|
||||
/*
|
||||
* Invert the interrupt polarity if a PMC device tree node
|
||||
|
|
|
@ -107,7 +107,8 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
|
|||
simple->pulse_resets = true;
|
||||
}
|
||||
|
||||
simple->resets = of_reset_control_array_get(np, shared_resets, true);
|
||||
simple->resets = of_reset_control_array_get(np, shared_resets, true,
|
||||
true);
|
||||
if (IS_ERR(simple->resets)) {
|
||||
ret = PTR_ERR(simple->resets);
|
||||
dev_err(dev, "failed to get device resets, err=%d\n", ret);
|
||||
|
|
|
@ -14,23 +14,26 @@ int reset_control_reset(struct reset_control *rstc);
|
|||
int reset_control_assert(struct reset_control *rstc);
|
||||
int reset_control_deassert(struct reset_control *rstc);
|
||||
int reset_control_status(struct reset_control *rstc);
|
||||
int reset_control_acquire(struct reset_control *rstc);
|
||||
void reset_control_release(struct reset_control *rstc);
|
||||
|
||||
struct reset_control *__of_reset_control_get(struct device_node *node,
|
||||
const char *id, int index, bool shared,
|
||||
bool optional);
|
||||
bool optional, bool acquired);
|
||||
struct reset_control *__reset_control_get(struct device *dev, const char *id,
|
||||
int index, bool shared,
|
||||
bool optional);
|
||||
bool optional, bool acquired);
|
||||
void reset_control_put(struct reset_control *rstc);
|
||||
int __device_reset(struct device *dev, bool optional);
|
||||
struct reset_control *__devm_reset_control_get(struct device *dev,
|
||||
const char *id, int index, bool shared,
|
||||
bool optional);
|
||||
bool optional, bool acquired);
|
||||
|
||||
struct reset_control *devm_reset_control_array_get(struct device *dev,
|
||||
bool shared, bool optional);
|
||||
struct reset_control *of_reset_control_array_get(struct device_node *np,
|
||||
bool shared, bool optional);
|
||||
bool shared, bool optional,
|
||||
bool acquired);
|
||||
|
||||
int reset_control_get_count(struct device *dev);
|
||||
|
||||
|
@ -56,6 +59,15 @@ static inline int reset_control_status(struct reset_control *rstc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int reset_control_acquire(struct reset_control *rstc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void reset_control_release(struct reset_control *rstc)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void reset_control_put(struct reset_control *rstc)
|
||||
{
|
||||
}
|
||||
|
@ -68,21 +80,23 @@ static inline int __device_reset(struct device *dev, bool optional)
|
|||
static inline struct reset_control *__of_reset_control_get(
|
||||
struct device_node *node,
|
||||
const char *id, int index, bool shared,
|
||||
bool optional)
|
||||
bool optional, bool acquired)
|
||||
{
|
||||
return optional ? NULL : ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline struct reset_control *__reset_control_get(
|
||||
struct device *dev, const char *id,
|
||||
int index, bool shared, bool optional)
|
||||
int index, bool shared, bool optional,
|
||||
bool acquired)
|
||||
{
|
||||
return optional ? NULL : ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline struct reset_control *__devm_reset_control_get(
|
||||
struct device *dev, const char *id,
|
||||
int index, bool shared, bool optional)
|
||||
int index, bool shared, bool optional,
|
||||
bool acquired)
|
||||
{
|
||||
return optional ? NULL : ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
@ -94,7 +108,8 @@ devm_reset_control_array_get(struct device *dev, bool shared, bool optional)
|
|||
}
|
||||
|
||||
static inline struct reset_control *
|
||||
of_reset_control_array_get(struct device_node *np, bool shared, bool optional)
|
||||
of_reset_control_array_get(struct device_node *np, bool shared, bool optional,
|
||||
bool acquired)
|
||||
{
|
||||
return optional ? NULL : ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
@ -134,7 +149,28 @@ static inline int device_reset_optional(struct device *dev)
|
|||
static inline struct reset_control *
|
||||
__must_check reset_control_get_exclusive(struct device *dev, const char *id)
|
||||
{
|
||||
return __reset_control_get(dev, id, 0, false, false);
|
||||
return __reset_control_get(dev, id, 0, false, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* reset_control_get_exclusive_released - Lookup and obtain a temoprarily
|
||||
* exclusive reference to a reset
|
||||
* controller.
|
||||
* @dev: device to be reset by the controller
|
||||
* @id: reset line name
|
||||
*
|
||||
* Returns a struct reset_control or IS_ERR() condition containing errno.
|
||||
* reset-controls returned by this function must be acquired via
|
||||
* reset_control_acquire() before they can be used and should be released
|
||||
* via reset_control_release() afterwards.
|
||||
*
|
||||
* Use of id names is optional.
|
||||
*/
|
||||
static inline struct reset_control *
|
||||
__must_check reset_control_get_exclusive_released(struct device *dev,
|
||||
const char *id)
|
||||
{
|
||||
return __reset_control_get(dev, id, 0, false, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -162,19 +198,19 @@ __must_check reset_control_get_exclusive(struct device *dev, const char *id)
|
|||
static inline struct reset_control *reset_control_get_shared(
|
||||
struct device *dev, const char *id)
|
||||
{
|
||||
return __reset_control_get(dev, id, 0, true, false);
|
||||
return __reset_control_get(dev, id, 0, true, false, false);
|
||||
}
|
||||
|
||||
static inline struct reset_control *reset_control_get_optional_exclusive(
|
||||
struct device *dev, const char *id)
|
||||
{
|
||||
return __reset_control_get(dev, id, 0, false, true);
|
||||
return __reset_control_get(dev, id, 0, false, true, true);
|
||||
}
|
||||
|
||||
static inline struct reset_control *reset_control_get_optional_shared(
|
||||
struct device *dev, const char *id)
|
||||
{
|
||||
return __reset_control_get(dev, id, 0, true, true);
|
||||
return __reset_control_get(dev, id, 0, true, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -190,7 +226,7 @@ static inline struct reset_control *reset_control_get_optional_shared(
|
|||
static inline struct reset_control *of_reset_control_get_exclusive(
|
||||
struct device_node *node, const char *id)
|
||||
{
|
||||
return __of_reset_control_get(node, id, 0, false, false);
|
||||
return __of_reset_control_get(node, id, 0, false, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -215,7 +251,7 @@ static inline struct reset_control *of_reset_control_get_exclusive(
|
|||
static inline struct reset_control *of_reset_control_get_shared(
|
||||
struct device_node *node, const char *id)
|
||||
{
|
||||
return __of_reset_control_get(node, id, 0, true, false);
|
||||
return __of_reset_control_get(node, id, 0, true, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -232,7 +268,7 @@ static inline struct reset_control *of_reset_control_get_shared(
|
|||
static inline struct reset_control *of_reset_control_get_exclusive_by_index(
|
||||
struct device_node *node, int index)
|
||||
{
|
||||
return __of_reset_control_get(node, NULL, index, false, false);
|
||||
return __of_reset_control_get(node, NULL, index, false, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -260,7 +296,7 @@ static inline struct reset_control *of_reset_control_get_exclusive_by_index(
|
|||
static inline struct reset_control *of_reset_control_get_shared_by_index(
|
||||
struct device_node *node, int index)
|
||||
{
|
||||
return __of_reset_control_get(node, NULL, index, true, false);
|
||||
return __of_reset_control_get(node, NULL, index, true, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -279,7 +315,26 @@ static inline struct reset_control *
|
|||
__must_check devm_reset_control_get_exclusive(struct device *dev,
|
||||
const char *id)
|
||||
{
|
||||
return __devm_reset_control_get(dev, id, 0, false, false);
|
||||
return __devm_reset_control_get(dev, id, 0, false, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_reset_control_get_exclusive_released - resource managed
|
||||
* reset_control_get_exclusive_released()
|
||||
* @dev: device to be reset by the controller
|
||||
* @id: reset line name
|
||||
*
|
||||
* Managed reset_control_get_exclusive_released(). For reset controllers
|
||||
* returned from this function, reset_control_put() is called automatically on
|
||||
* driver detach.
|
||||
*
|
||||
* See reset_control_get_exclusive_released() for more information.
|
||||
*/
|
||||
static inline struct reset_control *
|
||||
__must_check devm_reset_control_get_exclusive_released(struct device *dev,
|
||||
const char *id)
|
||||
{
|
||||
return __devm_reset_control_get(dev, id, 0, false, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -294,19 +349,19 @@ __must_check devm_reset_control_get_exclusive(struct device *dev,
|
|||
static inline struct reset_control *devm_reset_control_get_shared(
|
||||
struct device *dev, const char *id)
|
||||
{
|
||||
return __devm_reset_control_get(dev, id, 0, true, false);
|
||||
return __devm_reset_control_get(dev, id, 0, true, false, false);
|
||||
}
|
||||
|
||||
static inline struct reset_control *devm_reset_control_get_optional_exclusive(
|
||||
struct device *dev, const char *id)
|
||||
{
|
||||
return __devm_reset_control_get(dev, id, 0, false, true);
|
||||
return __devm_reset_control_get(dev, id, 0, false, true, true);
|
||||
}
|
||||
|
||||
static inline struct reset_control *devm_reset_control_get_optional_shared(
|
||||
struct device *dev, const char *id)
|
||||
{
|
||||
return __devm_reset_control_get(dev, id, 0, true, true);
|
||||
return __devm_reset_control_get(dev, id, 0, true, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -324,7 +379,7 @@ static inline struct reset_control *devm_reset_control_get_optional_shared(
|
|||
static inline struct reset_control *
|
||||
devm_reset_control_get_exclusive_by_index(struct device *dev, int index)
|
||||
{
|
||||
return __devm_reset_control_get(dev, NULL, index, false, false);
|
||||
return __devm_reset_control_get(dev, NULL, index, false, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -340,7 +395,7 @@ devm_reset_control_get_exclusive_by_index(struct device *dev, int index)
|
|||
static inline struct reset_control *
|
||||
devm_reset_control_get_shared_by_index(struct device *dev, int index)
|
||||
{
|
||||
return __devm_reset_control_get(dev, NULL, index, true, false);
|
||||
return __devm_reset_control_get(dev, NULL, index, true, false, false);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -412,24 +467,30 @@ devm_reset_control_array_get_optional_shared(struct device *dev)
|
|||
static inline struct reset_control *
|
||||
of_reset_control_array_get_exclusive(struct device_node *node)
|
||||
{
|
||||
return of_reset_control_array_get(node, false, false);
|
||||
return of_reset_control_array_get(node, false, false, true);
|
||||
}
|
||||
|
||||
static inline struct reset_control *
|
||||
of_reset_control_array_get_exclusive_released(struct device_node *node)
|
||||
{
|
||||
return of_reset_control_array_get(node, false, false, false);
|
||||
}
|
||||
|
||||
static inline struct reset_control *
|
||||
of_reset_control_array_get_shared(struct device_node *node)
|
||||
{
|
||||
return of_reset_control_array_get(node, true, false);
|
||||
return of_reset_control_array_get(node, true, false, true);
|
||||
}
|
||||
|
||||
static inline struct reset_control *
|
||||
of_reset_control_array_get_optional_exclusive(struct device_node *node)
|
||||
{
|
||||
return of_reset_control_array_get(node, false, true);
|
||||
return of_reset_control_array_get(node, false, true, true);
|
||||
}
|
||||
|
||||
static inline struct reset_control *
|
||||
of_reset_control_array_get_optional_shared(struct device_node *node)
|
||||
{
|
||||
return of_reset_control_array_get(node, true, true);
|
||||
return of_reset_control_array_get(node, true, true, true);
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue