Message ID | 1449848520-27379-1-git-send-email-hdegoede@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi Hans, thanks for moving this forward. Am Freitag, den 11.12.2015, 16:41 +0100 schrieb Hans de Goede: > Add reset_control_deassert_shared / reset_control_assert_shared > functions which are intended for use by drivers for hw blocks which > (may) share a reset line with another driver / hw block. > > Unlike the regular reset_control_[de]assert functions these functions > keep track of how often deassert_shared / assert_shared have been called > and keep the line deasserted as long as deassert has been called more > times than assert. > > Signed-off-by: Hans de Goede <hdegoede@redhat.com> > --- > Changes in v2: > -This is a new patch in v2 of this patch-set > --- > drivers/reset/core.c | 121 ++++++++++++++++++++++++++++++++++++--- > include/linux/reset-controller.h | 2 + > include/linux/reset.h | 2 + > 3 files changed, 116 insertions(+), 9 deletions(-) > > diff --git a/drivers/reset/core.c b/drivers/reset/core.c > index 9ab9290..8c3436c 100644 > --- a/drivers/reset/core.c > +++ b/drivers/reset/core.c > @@ -22,16 +22,29 @@ static DEFINE_MUTEX(reset_controller_list_mutex); > static LIST_HEAD(reset_controller_list); > > /** > + * struct reset_line - a reset line > + * @list: list entry for the reset controllers reset line list > + * @id: ID of the reset line in the reset controller device > + * @refcnt: Number of reset_control structs referencing this device > + * @deassert_cnt: Number of times this reset line has been deasserted > + */ > +struct reset_line { > + struct list_head list; > + unsigned int id; > + unsigned int refcnt; > + unsigned int deassert_cnt; > +}; I'd move rcdev into reset_line, too. That way the description is complete, and we don't duplicate rcdev when there are multiple reset_controls pointing here. > +/** > * struct reset_control - a reset control > * @rcdev: a pointer to the reset controller device > * this reset control belongs to > - * @id: ID of the reset controller in the reset > - * controller device > + * @line: reset line for this reset control > */ > struct reset_control { > struct reset_controller_dev *rcdev; > + struct reset_line *line; > struct device *dev; > - unsigned int id; > }; > > /** [...] > @@ -119,13 +134,55 @@ EXPORT_SYMBOL_GPL(reset_control_assert); > int reset_control_deassert(struct reset_control *rstc) > { Maybe WARN_ON(rstc->line->refcnt > 1) ? > if (rstc->rcdev->ops->deassert) > - return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); > + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id); > > return -ENOTSUPP; > } > EXPORT_SYMBOL_GPL(reset_control_deassert); > > /** > + * reset_control_assert_shared - asserts a shared reset line > + * @rstc: reset controller > + * > + * Assert a shared reset line, this functions decreases the deassert count > + * of the line by one and asserts it if, and only if, the deassert count > + * reaches 0. "After calling this function the shared reset line may be asserted, or it may still be deasserted, as long as other users keep it so." > + */ > +int reset_control_assert_shared(struct reset_control *rstc) > +{ > + if (!rstc->rcdev->ops->assert) > + return -ENOTSUPP; WARN_ON(rstc->line->deassert_cnt == 0) Actually, what to do in this case? Assume ops->assert was called, or do it again to be sure? Certainly we don't want to wrap deassert_cnt, or the next deassert_shared will do nothing. > + rstc->line->deassert_cnt--; > + if (rstc->line->deassert_cnt) deassert_cnt isn't protected by any lock. > + return 0; > + > + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->line->id); > +} > +EXPORT_SYMBOL_GPL(reset_control_assert_shared); > + > +/** > + * reset_control_deassert_shared - deasserts a shared reset line > + * @rstc: reset controller > + * > + * Assert a shared reset line, this functions increases the deassert count Deassert > + * of the line by one and deasserts the reset line (if it was not already > + * deasserted). "After calling this function, the shared reset line is guaranteed to be deasserted." > + */ > +int reset_control_deassert_shared(struct reset_control *rstc) > +{ > + if (!rstc->rcdev->ops->deassert) > + return -ENOTSUPP; > + > + rstc->line->deassert_cnt++; > + if (rstc->line->deassert_cnt != 1) > + return 0; > + > + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id); > +} > +EXPORT_SYMBOL_GPL(reset_control_deassert_shared); > + > +/** > * reset_control_status - returns a negative errno if not supported, a > * positive value if the reset line is asserted, or zero if the reset > * line is not asserted. > @@ -134,12 +191,47 @@ EXPORT_SYMBOL_GPL(reset_control_deassert); > int reset_control_status(struct reset_control *rstc) > { > if (rstc->rcdev->ops->status) > - return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); > + return rstc->rcdev->ops->status(rstc->rcdev, rstc->line->id); > > return -ENOTSUPP; > } > EXPORT_SYMBOL_GPL(reset_control_status); > > +static struct reset_line *reset_line_get(struct reset_controller_dev *rcdev, > + unsigned int index) > +{ > + struct reset_line *line; lockdep_assert_held here and in reset_line_put would document that these functions are called under reset_(controller_)list_mutex. > + list_for_each_entry(line, &rcdev->reset_line_head, list) { > + if (line->id == index) { > + line->refcnt++; > + return line; > + } > + } > + > + line = kzalloc(sizeof(*line), GFP_KERNEL); > + if (!line) > + return NULL; > + > + list_add(&line->list, &rcdev->reset_line_head); > + line->id = index; > + line->refcnt = 1; > + > + return line; > +} > + > +static void reset_line_put(struct reset_line *line) > +{ > + if (!line) > + return; > + > + if (--line->refcnt) > + return; > + > + list_del(&line->list); > + kfree(line); > +} > + > /** > * of_reset_control_get_by_index - Lookup and obtain a reference to a reset > * controller by index. > @@ -155,6 +247,7 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, > { > struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER); > struct reset_controller_dev *r, *rcdev; > + struct reset_line *line; > struct of_phandle_args args; > int rstc_id; > int ret; > @@ -186,16 +279,22 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, > } > > try_module_get(rcdev->owner); > + > + /* reset_controller_list_mutex also protects the reset_line list */ Let's rename it to reset_list_mutex, then. > + line = reset_line_get(rcdev, rstc_id); > + > mutex_unlock(&reset_controller_list_mutex); > > rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); > - if (!rstc) { > + if (!line || !rstc) { > + kfree(rstc); > + reset_line_put(line); > module_put(rcdev->owner); > return ERR_PTR(-ENOMEM); > } > > rstc->rcdev = rcdev; > - rstc->id = rstc_id; > + rstc->line = line; > > return rstc; > } > @@ -259,6 +358,10 @@ void reset_control_put(struct reset_control *rstc) > if (IS_ERR(rstc)) > return; > > + mutex_lock(&reset_controller_list_mutex); > + reset_line_put(rstc->line); > + mutex_unlock(&reset_controller_list_mutex); > + > module_put(rstc->rcdev->owner); > kfree(rstc); > } > diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h > index ce6b962..7f2cbd1 100644 > --- a/include/linux/reset-controller.h > +++ b/include/linux/reset-controller.h > @@ -31,6 +31,7 @@ struct of_phandle_args; > * @ops: a pointer to device specific struct reset_control_ops > * @owner: kernel module of the reset controller driver > * @list: internal list of reset controller devices > + * @reset_line_head: head of internal list of reset lines "list of requested reset lines" or "list of reset lines in use" to make clear the list only contains entries for the requested controls? > * @of_node: corresponding device tree node as phandle target > * @of_reset_n_cells: number of cells in reset line specifiers > * @of_xlate: translation function to translate from specifier as found in the [...] best regards Philipp
Hi, On 11-12-15 18:10, Philipp Zabel wrote: > Hi Hans, > > thanks for moving this forward. Thanks for the quick review, I've a couple of (simple) questions about your review remarks once those are cleared up I'll post a new version (of just this patch). > Am Freitag, den 11.12.2015, 16:41 +0100 schrieb Hans de Goede: >> Add reset_control_deassert_shared / reset_control_assert_shared >> functions which are intended for use by drivers for hw blocks which >> (may) share a reset line with another driver / hw block. >> >> Unlike the regular reset_control_[de]assert functions these functions >> keep track of how often deassert_shared / assert_shared have been called >> and keep the line deasserted as long as deassert has been called more >> times than assert. >> >> Signed-off-by: Hans de Goede <hdegoede@redhat.com> >> --- >> Changes in v2: >> -This is a new patch in v2 of this patch-set >> --- >> drivers/reset/core.c | 121 ++++++++++++++++++++++++++++++++++++--- >> include/linux/reset-controller.h | 2 + >> include/linux/reset.h | 2 + >> 3 files changed, 116 insertions(+), 9 deletions(-) >> >> diff --git a/drivers/reset/core.c b/drivers/reset/core.c >> index 9ab9290..8c3436c 100644 >> --- a/drivers/reset/core.c >> +++ b/drivers/reset/core.c >> @@ -22,16 +22,29 @@ static DEFINE_MUTEX(reset_controller_list_mutex); >> static LIST_HEAD(reset_controller_list); >> >> /** >> + * struct reset_line - a reset line >> + * @list: list entry for the reset controllers reset line list >> + * @id: ID of the reset line in the reset controller device >> + * @refcnt: Number of reset_control structs referencing this device >> + * @deassert_cnt: Number of times this reset line has been deasserted >> + */ >> +struct reset_line { >> + struct list_head list; >> + unsigned int id; >> + unsigned int refcnt; >> + unsigned int deassert_cnt; >> +}; > > I'd move rcdev into reset_line, too. That way the description is > complete, and we don't duplicate rcdev when there are multiple > reset_controls pointing here. Ack. >> +/** >> * struct reset_control - a reset control >> * @rcdev: a pointer to the reset controller device >> * this reset control belongs to >> - * @id: ID of the reset controller in the reset >> - * controller device >> + * @line: reset line for this reset control >> */ >> struct reset_control { >> struct reset_controller_dev *rcdev; >> + struct reset_line *line; >> struct device *dev; >> - unsigned int id; >> }; >> >> /** > [...] >> @@ -119,13 +134,55 @@ EXPORT_SYMBOL_GPL(reset_control_assert); >> int reset_control_deassert(struct reset_control *rstc) >> { > > Maybe WARN_ON(rstc->line->refcnt > 1) ? I assume you mean deasser_cnt ? Seems reasonable with that change. >> if (rstc->rcdev->ops->deassert) >> - return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); >> + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id); >> >> return -ENOTSUPP; >> } >> EXPORT_SYMBOL_GPL(reset_control_deassert); >> >> /** >> + * reset_control_assert_shared - asserts a shared reset line >> + * @rstc: reset controller >> + * >> + * Assert a shared reset line, this functions decreases the deassert count >> + * of the line by one and asserts it if, and only if, the deassert count >> + * reaches 0. > > "After calling this function the shared reset line may be asserted, or > it may still be deasserted, as long as other users keep it so." I take it this is to replace the text about "deassert count" ? >> + */ >> +int reset_control_assert_shared(struct reset_control *rstc) >> +{ >> + if (!rstc->rcdev->ops->assert) >> + return -ENOTSUPP; > > WARN_ON(rstc->line->deassert_cnt == 0) > > Actually, what to do in this case? Assume ops->assert was called, or do > it again to be sure? Certainly we don't want to wrap deassert_cnt, or > the next deassert_shared will do nothing. > >> + rstc->line->deassert_cnt--; >> + if (rstc->line->deassert_cnt) > > deassert_cnt isn't protected by any lock. Right, good catch. I believe the best way to fix this is to change deassert_cnt into an atomic_t and use atomic_dec_return / atomic_int_return, Downside of using an atomic_t is that doing the WARN_ON you are asking for above will not be race free, then, but since it is a should never happen scenario I guess we do not care about the check not being 100% race free. Or we can even just leave out the check ? > >> + return 0; >> + >> + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->line->id); >> +} >> +EXPORT_SYMBOL_GPL(reset_control_assert_shared); >> + >> +/** >> + * reset_control_deassert_shared - deasserts a shared reset line >> + * @rstc: reset controller >> + * >> + * Assert a shared reset line, this functions increases the deassert count > > Deassert Ack. >> + * of the line by one and deasserts the reset line (if it was not already >> + * deasserted). > > "After calling this function, the shared reset line is guaranteed to be > deasserted." Same question as above. >> + */ >> +int reset_control_deassert_shared(struct reset_control *rstc) >> +{ >> + if (!rstc->rcdev->ops->deassert) >> + return -ENOTSUPP; >> + >> + rstc->line->deassert_cnt++; >> + if (rstc->line->deassert_cnt != 1) >> + return 0; >> + >> + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id); >> +} >> +EXPORT_SYMBOL_GPL(reset_control_deassert_shared); >> + >> +/** >> * reset_control_status - returns a negative errno if not supported, a >> * positive value if the reset line is asserted, or zero if the reset >> * line is not asserted. >> @@ -134,12 +191,47 @@ EXPORT_SYMBOL_GPL(reset_control_deassert); >> int reset_control_status(struct reset_control *rstc) >> { >> if (rstc->rcdev->ops->status) >> - return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); >> + return rstc->rcdev->ops->status(rstc->rcdev, rstc->line->id); >> >> return -ENOTSUPP; >> } >> EXPORT_SYMBOL_GPL(reset_control_status); >> >> +static struct reset_line *reset_line_get(struct reset_controller_dev *rcdev, >> + unsigned int index) >> +{ >> + struct reset_line *line; > > lockdep_assert_held here and in reset_line_put would document that these > functions are called under reset_(controller_)list_mutex. Ok, I will add these. >> + list_for_each_entry(line, &rcdev->reset_line_head, list) { >> + if (line->id == index) { >> + line->refcnt++; >> + return line; >> + } >> + } >> + >> + line = kzalloc(sizeof(*line), GFP_KERNEL); >> + if (!line) >> + return NULL; >> + >> + list_add(&line->list, &rcdev->reset_line_head); >> + line->id = index; >> + line->refcnt = 1; >> + >> + return line; >> +} >> + >> +static void reset_line_put(struct reset_line *line) >> +{ >> + if (!line) >> + return; >> + >> + if (--line->refcnt) >> + return; >> + >> + list_del(&line->list); >> + kfree(line); >> +} >> + >> /** >> * of_reset_control_get_by_index - Lookup and obtain a reference to a reset >> * controller by index. >> @@ -155,6 +247,7 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, >> { >> struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER); >> struct reset_controller_dev *r, *rcdev; >> + struct reset_line *line; >> struct of_phandle_args args; >> int rstc_id; >> int ret; >> @@ -186,16 +279,22 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, >> } >> >> try_module_get(rcdev->owner); >> + >> + /* reset_controller_list_mutex also protects the reset_line list */ > > Let's rename it to reset_list_mutex, then. Ack. >> + line = reset_line_get(rcdev, rstc_id); >> + >> mutex_unlock(&reset_controller_list_mutex); >> >> rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); >> - if (!rstc) { >> + if (!line || !rstc) { >> + kfree(rstc); >> + reset_line_put(line); >> module_put(rcdev->owner); >> return ERR_PTR(-ENOMEM); >> } >> >> rstc->rcdev = rcdev; >> - rstc->id = rstc_id; >> + rstc->line = line; >> >> return rstc; >> } >> @@ -259,6 +358,10 @@ void reset_control_put(struct reset_control *rstc) >> if (IS_ERR(rstc)) >> return; >> >> + mutex_lock(&reset_controller_list_mutex); >> + reset_line_put(rstc->line); >> + mutex_unlock(&reset_controller_list_mutex); >> + >> module_put(rstc->rcdev->owner); >> kfree(rstc); >> } >> diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h >> index ce6b962..7f2cbd1 100644 >> --- a/include/linux/reset-controller.h >> +++ b/include/linux/reset-controller.h >> @@ -31,6 +31,7 @@ struct of_phandle_args; >> * @ops: a pointer to device specific struct reset_control_ops >> * @owner: kernel module of the reset controller driver >> * @list: internal list of reset controller devices >> + * @reset_line_head: head of internal list of reset lines > > "list of requested reset lines" or "list of reset lines in use" to make > clear the list only contains entries for the requested controls? Ok, will fix. >> * @of_node: corresponding device tree node as phandle target >> * @of_reset_n_cells: number of cells in reset line specifiers >> * @of_xlate: translation function to translate from specifier as found in the > [...] > > best regards > Philipp > Regards, Hans
Hi Hans, Am Freitag, den 11.12.2015, 19:21 +0100 schrieb Hans de Goede: [...] > >> @@ -119,13 +134,55 @@ EXPORT_SYMBOL_GPL(reset_control_assert); > >> int reset_control_deassert(struct reset_control *rstc) > >> { > > > > Maybe WARN_ON(rstc->line->refcnt > 1) ? > > I assume you mean deasser_cnt ? Seems reasonable with that change. I meant refcnt. Currently drivers sharing reset lines (refcnt > 1) and then using the non-shared reset control functions are bound to cause unexpected behaviour. Now we can detect this for the first time. > >> if (rstc->rcdev->ops->deassert) > >> - return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); > >> + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id); > >> > >> return -ENOTSUPP; > >> } > >> EXPORT_SYMBOL_GPL(reset_control_deassert); > >> > >> /** > >> + * reset_control_assert_shared - asserts a shared reset line > >> + * @rstc: reset controller > >> + * > >> + * Assert a shared reset line, this functions decreases the deassert count > >> + * of the line by one and asserts it if, and only if, the deassert count > >> + * reaches 0. > > > > "After calling this function the shared reset line may be asserted, or > > it may still be deasserted, as long as other users keep it so." > > I take it this is to replace the text about "deassert count" ? I thought you might append something like it. I just imagine that when reading the documentation it might be helpful to also see the intended effect described, especially given that a call to an _assert_ function might leave the reset line in deasserted state, depending on the refcount. > >> + */ > >> +int reset_control_assert_shared(struct reset_control *rstc) > >> +{ > >> + if (!rstc->rcdev->ops->assert) > >> + return -ENOTSUPP; > > > > WARN_ON(rstc->line->deassert_cnt == 0) > > > > Actually, what to do in this case? Assume ops->assert was called, or do > > it again to be sure? Certainly we don't want to wrap deassert_cnt, or > > the next deassert_shared will do nothing. > > > >> + rstc->line->deassert_cnt--; > >> + if (rstc->line->deassert_cnt) > > > > deassert_cnt isn't protected by any lock. > > Right, good catch. I believe the best way to fix this is to change deassert_cnt > into an atomic_t and use atomic_dec_return / atomic_int_return, Yes, that would work. > Downside of using an atomic_t is that doing the WARN_ON you are asking for above > will not be race free, then, but since it is a should never happen scenario I > guess we do not care about the check not being 100% race free. Or we can even > just leave out the check ? Since this is only a warning to notify driver developers we don't support shared resets (apart from the clock-like use) Not if we warn about refcnt instead of deassert_cnt above. [...] > >> + * of the line by one and deasserts the reset line (if it was not already > >> + * deasserted). > > > > "After calling this function, the shared reset line is guaranteed to be > > deasserted." > > Same question as above. Same imprecise answer. I'd like to see the expected state after calling this function in the description, in addition to the mechanism. This is more important for the assert function. After calling deassert, the reset line is deasserted, no reason to be surprised about that. regards Philipp
Hi, On Fri, Dec 11, 2015 at 04:41:58PM +0100, Hans de Goede wrote: > diff --git a/include/linux/reset.h b/include/linux/reset.h > index c4c097d..1cca8ce 100644 > --- a/include/linux/reset.h > +++ b/include/linux/reset.h > @@ -11,6 +11,8 @@ 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_assert_shared(struct reset_control *rstc); > +int reset_control_deassert_shared(struct reset_control *rstc); Shouldn't that be handled in reset_control_get directly? That would allow to share more code between the implementations, since it would just be a flag to test in the get and not duplicate the function definitions, and we could simply rely on the refcount to know if we have to actually assert / deassert the device, in all the cases (shared or not). That would also allow us to catch easily if we're going to be able a shared reset line or not, with the regular reset_control_get being exclusive, and reset_control_get_shared or reset_control_get(.., RESET_SHARED) being shared. It would also avoid ending up with the shared variant used in every generic driver eventually. We could just use the variant to see if we have to use the shared get or not, and the rest of the driver is left untouched, which is way less intrusive. Moreover, it's also pretty much the pattern used everywhere else (irqs, regulator, clocks, etc.), so it's going to be easier to review as well. Maxime
Am Montag, den 14.12.2015, 10:36 +0100 schrieb Maxime Ripard: > Hi, > > On Fri, Dec 11, 2015 at 04:41:58PM +0100, Hans de Goede wrote: > > diff --git a/include/linux/reset.h b/include/linux/reset.h > > index c4c097d..1cca8ce 100644 > > --- a/include/linux/reset.h > > +++ b/include/linux/reset.h > > @@ -11,6 +11,8 @@ 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_assert_shared(struct reset_control *rstc); > > +int reset_control_deassert_shared(struct reset_control *rstc); > > Shouldn't that be handled in reset_control_get directly? This is about different expectations of the caller. A driver calling reset_control_assert expects the reset line to be asserted after the call. A driver calling reset_control_assert_shared just signals that it doesn't care about the state of the reset line anymore. We could just as well call the two new functions reset_control_deassert_get and reset_control_deassert_put. regards Philipp
On Mon, Dec 14, 2015 at 10:50:55AM +0100, Philipp Zabel wrote: > Am Montag, den 14.12.2015, 10:36 +0100 schrieb Maxime Ripard: > > Hi, > > > > On Fri, Dec 11, 2015 at 04:41:58PM +0100, Hans de Goede wrote: > > > diff --git a/include/linux/reset.h b/include/linux/reset.h > > > index c4c097d..1cca8ce 100644 > > > --- a/include/linux/reset.h > > > +++ b/include/linux/reset.h > > > @@ -11,6 +11,8 @@ 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_assert_shared(struct reset_control *rstc); > > > +int reset_control_deassert_shared(struct reset_control *rstc); > > > > Shouldn't that be handled in reset_control_get directly? > > This is about different expectations of the caller. > A driver calling reset_control_assert expects the reset line to be > asserted after the call. Is that behaviour documented explicitly somewhere? > A driver calling reset_control_assert_shared > just signals that it doesn't care about the state of the reset line > anymore. > We could just as well call the two new functions > reset_control_deassert_get and reset_control_deassert_put. What happens if you mix them? What happens if you have several drivers ignoring this API? The current default API totally allows to have several drivers getting the same reset line, and happily poking that reset line without any way for the others to A) know they're not the single users B) let them know their device has been reset. And not being able to tell at the consumer level if and when our device is going to be reset behind our back is a big issue. Because then, we simply have to expect it can be reset at any point in time, good luck writing a driver with that expectation. The reset framework should make sure that the shared case is an exception, and not the default case (and make sure that it cannot happen in the default case). Just like for any other framework with similar resources constraints. Maxime
Hi Maxime, Am Mittwoch, den 16.12.2015, 11:29 +0100 schrieb Maxime Ripard: > On Mon, Dec 14, 2015 at 10:50:55AM +0100, Philipp Zabel wrote: > > Am Montag, den 14.12.2015, 10:36 +0100 schrieb Maxime Ripard: > > > Hi, > > > > > > On Fri, Dec 11, 2015 at 04:41:58PM +0100, Hans de Goede wrote: > > > > diff --git a/include/linux/reset.h b/include/linux/reset.h > > > > index c4c097d..1cca8ce 100644 > > > > --- a/include/linux/reset.h > > > > +++ b/include/linux/reset.h > > > > @@ -11,6 +11,8 @@ 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_assert_shared(struct reset_control *rstc); > > > > +int reset_control_deassert_shared(struct reset_control *rstc); > > > > > > Shouldn't that be handled in reset_control_get directly? I think I see your point now. Maybe we should add a flags parameter to reset_control_get and/or wrap it in two versions, reset_control_get_exclusive and reset_control_get_shared (or just add the _shared variant). Then reset_control_get(_exclusive) could return -EBUSY if a reset line is already in use. > > This is about different expectations of the caller. > > A driver calling reset_control_assert expects the reset line to be > > asserted after the call. > > Is that behaviour documented explicitly somewhere? /** * reset_control_assert - asserts the reset line * @rstc: reset controller */ Also, that expected behavior matches the function name, which I like. So I still welcome adding new API calls for the shared/refcounting variant. > > A driver calling reset_control_assert_shared > > just signals that it doesn't care about the state of the reset line > > anymore. > > We could just as well call the two new functions > > reset_control_deassert_get and reset_control_deassert_put. > > What happens if you mix them? What happens if you have several drivers > ignoring this API? The core should give useful error messages and disallow non-shared reset calls on shared lines. > The current default API totally allows to have several drivers getting > the same reset line, and happily poking that reset line without any > way for the others to A) know they're not the single users B) let them > know their device has been reset. That's why I'd like the WARN_ON and error return in reset_control_* when the reset_line reference count is > 1. > And not being able to tell at the consumer level if and when our > device is going to be reset behind our back is a big issue. Because > then, we simply have to expect it can be reset at any point in time, > good luck writing a driver with that expectation. Yes, that is unacceptable. > The reset framework should make sure that the shared case is an > exception, and not the default case (and make sure that it cannot > happen in the default case). Just like for any other framework with > similar resources constraints. regards Philipp
On Wed, Dec 16, 2015 at 12:21:48PM +0100, Philipp Zabel wrote: > Hi Maxime, > > Am Mittwoch, den 16.12.2015, 11:29 +0100 schrieb Maxime Ripard: > > On Mon, Dec 14, 2015 at 10:50:55AM +0100, Philipp Zabel wrote: > > > Am Montag, den 14.12.2015, 10:36 +0100 schrieb Maxime Ripard: > > > > Hi, > > > > > > > > On Fri, Dec 11, 2015 at 04:41:58PM +0100, Hans de Goede wrote: > > > > > diff --git a/include/linux/reset.h b/include/linux/reset.h > > > > > index c4c097d..1cca8ce 100644 > > > > > --- a/include/linux/reset.h > > > > > +++ b/include/linux/reset.h > > > > > @@ -11,6 +11,8 @@ 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_assert_shared(struct reset_control *rstc); > > > > > +int reset_control_deassert_shared(struct reset_control *rstc); > > > > > > > > Shouldn't that be handled in reset_control_get directly? > > I think I see your point now. Maybe we should add a flags parameter to > reset_control_get and/or wrap it in two versions, > reset_control_get_exclusive and reset_control_get_shared (or just add > the _shared variant). Then reset_control_get(_exclusive) could return > -EBUSY if a reset line is already in use. I guess the current assumption was that reset_control_get was exclusive. So we could have something like: reset_control_get (..) { return __reset_control_get(.., 0); } reset_control_get_shared (..) { return __reset_control_get(.., RESET_SHARED); } And all the logic shared between the two, without exposing any flag (that would change the prototype and require to fix every callers). > > > > This is about different expectations of the caller. > > > A driver calling reset_control_assert expects the reset line to be > > > asserted after the call. > > > > Is that behaviour documented explicitly somewhere? > > /** > * reset_control_assert - asserts the reset line > * @rstc: reset controller > */ Yeah, but it's not said when it would happen, or if it happens synchronously. > Also, that expected behavior matches the function name, which I like. > So I still welcome adding new API calls for the shared/refcounting > variant. > > > > A driver calling reset_control_assert_shared > > > just signals that it doesn't care about the state of the reset line > > > anymore. > > > We could just as well call the two new functions > > > reset_control_deassert_get and reset_control_deassert_put. > > > > What happens if you mix them? What happens if you have several drivers > > ignoring this API? > > The core should give useful error messages and disallow non-shared reset > calls on shared lines. I guess we could also have something like this: * The driver gets the reference to the reset line using reset_control_get or its shared variant. - If you call reset_control_get on a free line, it succeeds, and marks the line in exclusive use. - If you call reset_control_get on a busy line, it fails with EBUSY - If you call the shared variant on a free line, it succeeds - If you call the shared variant on a busy exclusive line, it fails with EBUSY - If you call the shared variant on a busy !exclusive line, it succeeds. * The customer driver can then call reset_control_assert / deassert: - If the reset line is in exclusive use, the assertion happens right away, it succeeds and returns 0. - If the reset line is in a !exclusive use, but with a single user, the assertion happens right away, it succeeds and returns 0. - If the reset line is in a !exclusive use with more than 1 user, the refcount is modified and an error is returned to notify that it didn't happen. What do you think? That would work fine in both the case where you care about the fact that the device has really been asserted (to recover from an error for example) and the one when you don't really care and you just want to give away your reset line (in a remove for example). I'd really like to avoid having shared variants of assert and de-assert, since it will only spread out and end up being used in drivers where most users don't have a shared reset line. And if we want to avoid that, we'll end up in something like if (struct->shared_reset) reset_control_assert_shared else reset_control_assert around *all* the calls in your driver. Maxime
Hi, On 18-12-15 12:08, Maxime Ripard wrote: > On Wed, Dec 16, 2015 at 12:21:48PM +0100, Philipp Zabel wrote: >> Hi Maxime, >> >> Am Mittwoch, den 16.12.2015, 11:29 +0100 schrieb Maxime Ripard: >>> On Mon, Dec 14, 2015 at 10:50:55AM +0100, Philipp Zabel wrote: >>>> Am Montag, den 14.12.2015, 10:36 +0100 schrieb Maxime Ripard: >>>>> Hi, >>>>> >>>>> On Fri, Dec 11, 2015 at 04:41:58PM +0100, Hans de Goede wrote: >>>>>> diff --git a/include/linux/reset.h b/include/linux/reset.h >>>>>> index c4c097d..1cca8ce 100644 >>>>>> --- a/include/linux/reset.h >>>>>> +++ b/include/linux/reset.h >>>>>> @@ -11,6 +11,8 @@ 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_assert_shared(struct reset_control *rstc); >>>>>> +int reset_control_deassert_shared(struct reset_control *rstc); >>>>> >>>>> Shouldn't that be handled in reset_control_get directly? >> >> I think I see your point now. Maybe we should add a flags parameter to >> reset_control_get and/or wrap it in two versions, >> reset_control_get_exclusive and reset_control_get_shared (or just add >> the _shared variant). Then reset_control_get(_exclusive) could return >> -EBUSY if a reset line is already in use. > > I guess the current assumption was that reset_control_get was > exclusive. > > So we could have something like: > > reset_control_get (..) { > return __reset_control_get(.., 0); > } > > reset_control_get_shared (..) { > return __reset_control_get(.., RESET_SHARED); > } > > And all the logic shared between the two, without exposing any flag > (that would change the prototype and require to fix every callers). > >> >>>> This is about different expectations of the caller. >>>> A driver calling reset_control_assert expects the reset line to be >>>> asserted after the call. >>> >>> Is that behaviour documented explicitly somewhere? >> >> /** >> * reset_control_assert - asserts the reset line >> * @rstc: reset controller >> */ > > Yeah, but it's not said when it would happen, or if it happens > synchronously. > >> Also, that expected behavior matches the function name, which I like. >> So I still welcome adding new API calls for the shared/refcounting >> variant. >> >>>> A driver calling reset_control_assert_shared >>>> just signals that it doesn't care about the state of the reset line >>>> anymore. >>>> We could just as well call the two new functions >>>> reset_control_deassert_get and reset_control_deassert_put. >>> >>> What happens if you mix them? What happens if you have several drivers >>> ignoring this API? >> >> The core should give useful error messages and disallow non-shared reset >> calls on shared lines. > > I guess we could also have something like this: > > * The driver gets the reference to the reset line using > reset_control_get or its shared variant. > > - If you call reset_control_get on a free line, it succeeds, and > marks the line in exclusive use. > - If you call reset_control_get on a busy line, it fails with > EBUSY > > - If you call the shared variant on a free line, it succeeds > - If you call the shared variant on a busy exclusive line, it > fails with EBUSY > - If you call the shared variant on a busy !exclusive line, it > succeeds. > > * The customer driver can then call reset_control_assert / deassert: > > - If the reset line is in exclusive use, the assertion happens > right away, it succeeds and returns 0. > > - If the reset line is in a !exclusive use, but with a single > user, the assertion happens right away, it succeeds and returns > 0. Ack for all of the above, this is what I had in mind at first, but I started with a more lightweight version as I'm lazy :) If Philipp likes this suggestion I can rework my patch-set into this. > - If the reset line is in a !exclusive use with more than 1 user, > the refcount is modified and an error is returned to notify that > it didn't happen. Also ack, except for returning the error, if a driver has used reset_control_get_shared, it should simply be aware that doing an assert might not necessarily actually assert the line, just like doing a clk-disable does not really necessary disable the clock, etc. If a driver is not prepared to deal with this, it should simply not use reset_control_get_shared. I see returning an error if the assert did not happen due to other users / deassert_count != 0 as inconsistent compared to how clks, regulators and phys handle this, these all simply return success in this case. Regards, Hans
Am Samstag, den 19.12.2015, 11:55 +0100 schrieb Hans de Goede: > On 18-12-15 12:08, Maxime Ripard wrote: [...] > > I guess we could also have something like this: > > > > * The driver gets the reference to the reset line using > > reset_control_get or its shared variant. > > > > - If you call reset_control_get on a free line, it succeeds, and > > marks the line in exclusive use. > > - If you call reset_control_get on a busy line, it fails with > > EBUSY > > > > - If you call the shared variant on a free line, it succeeds > > - If you call the shared variant on a busy exclusive line, it > > fails with EBUSY > > - If you call the shared variant on a busy !exclusive line, it > > succeeds. >> > > * The customer driver can then call reset_control_assert / deassert: > > > > - If the reset line is in exclusive use, the assertion happens > > right away, it succeeds and returns 0. > > > > - If the reset line is in a !exclusive use, but with a single > > user, the assertion happens right away, it succeeds and returns > > 0. > > Ack for all of the above, this is what I had in mind at first, but I started > with a more lightweight version as I'm lazy :) If Philipp likes this > suggestion I can rework my patch-set into this. Seconded, this all sounds good to me. > > - If the reset line is in a !exclusive use with more than 1 user, > > the refcount is modified and an error is returned to notify that > > it didn't happen. > > Also ack, except for returning the error, if a driver has used > reset_control_get_shared, it should simply be aware that doing an assert > might not necessarily actually assert the line, just like doing a clk-disable > does not really necessary disable the clock, etc. If a driver is not prepared > to deal with this, it should simply not use reset_control_get_shared. > > I see returning an error if the assert did not happen due to other users / > deassert_count != 0 as inconsistent compared to how clks, regulators and phys > handle this, these all simply return success in this case. I wouldn't want drivers to have to differentiate between relevant and irrelevant error codes, so in the clock-like refcounting use case reset_assert should not return an error if it just correctly decremented the refcount. I'd still prefer to have separate API for the counted must_deassert/may_assert vs the exclusive must_assert/must_deassert use cases, but I just can't think of a good name. regards Philipp
On 4 January 2016 at 21:39, Philipp Zabel <p.zabel@pengutronix.de> wrote: > Am Samstag, den 19.12.2015, 11:55 +0100 schrieb Hans de Goede: >> On 18-12-15 12:08, Maxime Ripard wrote: >> > - If the reset line is in a !exclusive use with more than 1 user, >> > the refcount is modified and an error is returned to notify that >> > it didn't happen. >> >> Also ack, except for returning the error, if a driver has used >> reset_control_get_shared, it should simply be aware that doing an assert >> might not necessarily actually assert the line, just like doing a clk-disable >> does not really necessary disable the clock, etc. If a driver is not prepared >> to deal with this, it should simply not use reset_control_get_shared. >> >> I see returning an error if the assert did not happen due to other users / >> deassert_count != 0 as inconsistent compared to how clks, regulators and phys >> handle this, these all simply return success in this case. > > I wouldn't want drivers to have to differentiate between relevant and > irrelevant error codes, so in the clock-like refcounting use case > reset_assert should not return an error if it just correctly decremented > the refcount. I'd still prefer to have separate API for the counted > must_deassert/may_assert vs the exclusive must_assert/must_deassert use > cases, but I just can't think of a good name. > Maybe something along the lines of assert_now or assert_sync. It should be possible to call on shared line and then get an error when the operation is blocked by other user. The driver may not really care. Depending on the hardware the line can be shared on one device and exclusive on another. The driver may just let the line go when the device is powered off. And it may require a reset cycle when it detects the device is hosed and return an error when the reset fails for whatever reason. Thanks Michal
diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 9ab9290..8c3436c 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -22,16 +22,29 @@ static DEFINE_MUTEX(reset_controller_list_mutex); static LIST_HEAD(reset_controller_list); /** + * struct reset_line - a reset line + * @list: list entry for the reset controllers reset line list + * @id: ID of the reset line in the reset controller device + * @refcnt: Number of reset_control structs referencing this device + * @deassert_cnt: Number of times this reset line has been deasserted + */ +struct reset_line { + struct list_head list; + unsigned int id; + unsigned int refcnt; + unsigned int deassert_cnt; +}; + +/** * struct reset_control - a reset control * @rcdev: a pointer to the reset controller device * this reset control belongs to - * @id: ID of the reset controller in the reset - * controller device + * @line: reset line for this reset control */ struct reset_control { struct reset_controller_dev *rcdev; + struct reset_line *line; struct device *dev; - unsigned int id; }; /** @@ -66,6 +79,8 @@ int reset_controller_register(struct reset_controller_dev *rcdev) rcdev->of_xlate = of_reset_simple_xlate; } + INIT_LIST_HEAD(&rcdev->reset_line_head); + mutex_lock(&reset_controller_list_mutex); list_add(&rcdev->list, &reset_controller_list); mutex_unlock(&reset_controller_list_mutex); @@ -93,7 +108,7 @@ EXPORT_SYMBOL_GPL(reset_controller_unregister); int reset_control_reset(struct reset_control *rstc) { if (rstc->rcdev->ops->reset) - return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); + return rstc->rcdev->ops->reset(rstc->rcdev, rstc->line->id); return -ENOTSUPP; } @@ -106,7 +121,7 @@ EXPORT_SYMBOL_GPL(reset_control_reset); int reset_control_assert(struct reset_control *rstc) { if (rstc->rcdev->ops->assert) - return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->line->id); return -ENOTSUPP; } @@ -119,13 +134,55 @@ EXPORT_SYMBOL_GPL(reset_control_assert); int reset_control_deassert(struct reset_control *rstc) { if (rstc->rcdev->ops->deassert) - return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id); return -ENOTSUPP; } EXPORT_SYMBOL_GPL(reset_control_deassert); /** + * reset_control_assert_shared - asserts a shared reset line + * @rstc: reset controller + * + * Assert a shared reset line, this functions decreases the deassert count + * of the line by one and asserts it if, and only if, the deassert count + * reaches 0. + */ +int reset_control_assert_shared(struct reset_control *rstc) +{ + if (!rstc->rcdev->ops->assert) + return -ENOTSUPP; + + rstc->line->deassert_cnt--; + if (rstc->line->deassert_cnt) + return 0; + + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->line->id); +} +EXPORT_SYMBOL_GPL(reset_control_assert_shared); + +/** + * reset_control_deassert_shared - deasserts a shared reset line + * @rstc: reset controller + * + * Assert a shared reset line, this functions increases the deassert count + * of the line by one and deasserts the reset line (if it was not already + * deasserted). + */ +int reset_control_deassert_shared(struct reset_control *rstc) +{ + if (!rstc->rcdev->ops->deassert) + return -ENOTSUPP; + + rstc->line->deassert_cnt++; + if (rstc->line->deassert_cnt != 1) + return 0; + + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id); +} +EXPORT_SYMBOL_GPL(reset_control_deassert_shared); + +/** * reset_control_status - returns a negative errno if not supported, a * positive value if the reset line is asserted, or zero if the reset * line is not asserted. @@ -134,12 +191,47 @@ EXPORT_SYMBOL_GPL(reset_control_deassert); int reset_control_status(struct reset_control *rstc) { if (rstc->rcdev->ops->status) - return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); + return rstc->rcdev->ops->status(rstc->rcdev, rstc->line->id); return -ENOTSUPP; } EXPORT_SYMBOL_GPL(reset_control_status); +static struct reset_line *reset_line_get(struct reset_controller_dev *rcdev, + unsigned int index) +{ + struct reset_line *line; + + list_for_each_entry(line, &rcdev->reset_line_head, list) { + if (line->id == index) { + line->refcnt++; + return line; + } + } + + line = kzalloc(sizeof(*line), GFP_KERNEL); + if (!line) + return NULL; + + list_add(&line->list, &rcdev->reset_line_head); + line->id = index; + line->refcnt = 1; + + return line; +} + +static void reset_line_put(struct reset_line *line) +{ + if (!line) + return; + + if (--line->refcnt) + return; + + list_del(&line->list); + kfree(line); +} + /** * of_reset_control_get_by_index - Lookup and obtain a reference to a reset * controller by index. @@ -155,6 +247,7 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, { struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER); struct reset_controller_dev *r, *rcdev; + struct reset_line *line; struct of_phandle_args args; int rstc_id; int ret; @@ -186,16 +279,22 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, } try_module_get(rcdev->owner); + + /* reset_controller_list_mutex also protects the reset_line list */ + line = reset_line_get(rcdev, rstc_id); + mutex_unlock(&reset_controller_list_mutex); rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); - if (!rstc) { + if (!line || !rstc) { + kfree(rstc); + reset_line_put(line); module_put(rcdev->owner); return ERR_PTR(-ENOMEM); } rstc->rcdev = rcdev; - rstc->id = rstc_id; + rstc->line = line; return rstc; } @@ -259,6 +358,10 @@ void reset_control_put(struct reset_control *rstc) if (IS_ERR(rstc)) return; + mutex_lock(&reset_controller_list_mutex); + reset_line_put(rstc->line); + mutex_unlock(&reset_controller_list_mutex); + module_put(rstc->rcdev->owner); kfree(rstc); } diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index ce6b962..7f2cbd1 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h @@ -31,6 +31,7 @@ struct of_phandle_args; * @ops: a pointer to device specific struct reset_control_ops * @owner: kernel module of the reset controller driver * @list: internal list of reset controller devices + * @reset_line_head: head of internal list of reset lines * @of_node: corresponding device tree node as phandle target * @of_reset_n_cells: number of cells in reset line specifiers * @of_xlate: translation function to translate from specifier as found in the @@ -41,6 +42,7 @@ struct reset_controller_dev { struct reset_control_ops *ops; struct module *owner; struct list_head list; + struct list_head reset_line_head; struct device_node *of_node; int of_reset_n_cells; int (*of_xlate)(struct reset_controller_dev *rcdev, diff --git a/include/linux/reset.h b/include/linux/reset.h index c4c097d..1cca8ce 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -11,6 +11,8 @@ 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_assert_shared(struct reset_control *rstc); +int reset_control_deassert_shared(struct reset_control *rstc); struct reset_control *reset_control_get(struct device *dev, const char *id); void reset_control_put(struct reset_control *rstc);
Add reset_control_deassert_shared / reset_control_assert_shared functions which are intended for use by drivers for hw blocks which (may) share a reset line with another driver / hw block. Unlike the regular reset_control_[de]assert functions these functions keep track of how often deassert_shared / assert_shared have been called and keep the line deasserted as long as deassert has been called more times than assert. Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- Changes in v2: -This is a new patch in v2 of this patch-set --- drivers/reset/core.c | 121 ++++++++++++++++++++++++++++++++++++--- include/linux/reset-controller.h | 2 + include/linux/reset.h | 2 + 3 files changed, 116 insertions(+), 9 deletions(-)