diff mbox

[v4,2/8] reset: Add reset controller API

Message ID 1361878774-6382-3-git-send-email-p.zabel@pengutronix.de (mailing list archive)
State New, archived
Headers show

Commit Message

Philipp Zabel Feb. 26, 2013, 11:39 a.m. UTC
This adds a simple API for devices to request being reset
by separate reset controller hardware and implements the
reset signal device tree binding.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: Stephen Warren <swarren@nvidia.com>
Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
---
Changes since v3:
 - Added of_xlate and nr_resets to reset_controller_dev,
   and of_reset_simple_xlate.
 - Added devm_put_reset_controller.
 - Put the rcdev owner module if rstc kzalloc fails.
---
 drivers/Kconfig                  |    2 +
 drivers/Makefile                 |    3 +
 drivers/reset/Kconfig            |   13 ++
 drivers/reset/Makefile           |    1 +
 drivers/reset/core.c             |  298 ++++++++++++++++++++++++++++++++++++++
 include/linux/reset-controller.h |   51 +++++++
 include/linux/reset.h            |   17 +++
 7 files changed, 385 insertions(+)
 create mode 100644 drivers/reset/Kconfig
 create mode 100644 drivers/reset/Makefile
 create mode 100644 drivers/reset/core.c
 create mode 100644 include/linux/reset-controller.h
 create mode 100644 include/linux/reset.h

Comments

Stephen Warren March 1, 2013, 8 p.m. UTC | #1
On 02/26/2013 04:39 AM, Philipp Zabel wrote:
> This adds a simple API for devices to request being reset
> by separate reset controller hardware and implements the
> reset signal device tree binding.

> diff --git a/drivers/reset/core.c b/drivers/reset/core.c

> +int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
> +			  const struct of_phandle_args *reset_spec, u32 *flags)
> +{
> +	if (WARN_ON(reset_spec->args_count < rcdev->of_reset_n_cells))
> +		return -EINVAL;

Would != make more sense than < ?

> +
> +	if (reset_spec->args[0] >= rcdev->nr_resets)
> +		return -EINVAL;
> +
> +	if (flags)
> +		*flags = reset_spec->args[1];

if (flags)
    if (reset_spec->args_count > 1)
        *flags = reset_spec->args[1];
    else
        *flags = 0;

?

> +struct reset_control *reset_control_get(struct device *dev, const char *id)
...
> +	mutex_lock(&reset_controller_list_mutex);
> +	rcdev = NULL;
> +	list_for_each_entry(r, &reset_controller_list, list) {
> +		if (args.np == r->of_node) {
> +			rcdev = r;
> +			break;
> +		}
> +	}
> +	mutex_unlock(&reset_controller_list_mutex);

At this point, rcdev could be removed from that list, and perhaps even
start to point at free'd memory.

> +	of_node_put(args.np);
> +
> +	if (!rcdev)
> +		return ERR_PTR(-ENODEV);
> +
> +	rstc_id = rcdev->of_xlate(rcdev, &args, NULL);
> +	if (rstc_id < 0)
> +		return ERR_PTR(rstc_id);
> +
> +	try_module_get(rcdev->owner);

What about error-handling here?

I think you want to drop reset_controller_list_mutex only after the call
to try_module_get()?

> +static int devm_reset_control_match(struct device *dev, void *res, void *data)
> +{
> +	struct reset_control **rstc = res;
> +	if (!rstc || !*rstc) {
> +		WARN_ON(!rstc || !*rstc);

I think you can if (WARN_ON(...)).

I'm not sure if the error-checks are quite right though;
reset_control_get always returns an error-pointer for errors, never
NULL, so the pointer can't ever be NULL. If it somehow was (e.g. client
usage error), then that NULL pointer would never match anything, so the
error-check still wouldn't be useful.

I'm not sure why this is a ** here; below in devm_reset_control_put()
you pass a just a *; are you expected to pass &rstc there instead?

> +void devm_reset_control_put(struct reset_control *rstc)
> +{
> +	int ret;
> +
> +	ret = devres_release(rstc->dev, devm_reset_control_release,
> +			     devm_reset_control_match, rstc);
> +	if (ret)
> +		WARN_ON(ret);
> +}
> +EXPORT_SYMBOL_GPL(devm_reset_control_put);
Philipp Zabel March 4, 2013, 8:33 a.m. UTC | #2
Hi Stephen,

Am Freitag, den 01.03.2013, 13:00 -0700 schrieb Stephen Warren:
> On 02/26/2013 04:39 AM, Philipp Zabel wrote:
> > This adds a simple API for devices to request being reset
> > by separate reset controller hardware and implements the
> > reset signal device tree binding.
> 
> > diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> 
> > +int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
> > +			  const struct of_phandle_args *reset_spec, u32 *flags)
> > +{
> > +	if (WARN_ON(reset_spec->args_count < rcdev->of_reset_n_cells))
> > +		return -EINVAL;
> 
> Would != make more sense than < ?

I copied this from of_gpio_simple_xlate, because the parsing will work
if args_count > of_reset_n_cells. In this case the device tree contains
a #reset-cells larger than what the reset controller driver expects.
Is this reason enough to assume the whole reset_spec is invalid?

> > +
> > +	if (reset_spec->args[0] >= rcdev->nr_resets)
> > +		return -EINVAL;
> > +
> > +	if (flags)
> > +		*flags = reset_spec->args[1];
> 
> if (flags)
>     if (reset_spec->args_count > 1)
>         *flags = reset_spec->args[1];
>     else
>         *flags = 0;
> 
> ?

In gpiolib, of_get_named_gpio_flags clears the flags, so that the xlate
implementations don't have to do it. The core doesn't use the flags, so
this is not a problem. I don't see the need for
of_get_named_reset_flags, since the reset consumer drivers shouldn't
care for those. Maybe it would be better to remove the flags parameter
from the xlate function altogether.

> > +struct reset_control *reset_control_get(struct device *dev, const char *id)
> ...
> > +	mutex_lock(&reset_controller_list_mutex);
> > +	rcdev = NULL;
> > +	list_for_each_entry(r, &reset_controller_list, list) {
> > +		if (args.np == r->of_node) {
> > +			rcdev = r;
> > +			break;
> > +		}
> > +	}
> > +	mutex_unlock(&reset_controller_list_mutex);
> 
> At this point, rcdev could be removed from that list, and perhaps even
> start to point at free'd memory.

I'll move the unlock down.

> > +	of_node_put(args.np);
> > +
> > +	if (!rcdev)
> > +		return ERR_PTR(-ENODEV);
> > +
> > +	rstc_id = rcdev->of_xlate(rcdev, &args, NULL);
> > +	if (rstc_id < 0)
> > +		return ERR_PTR(rstc_id);
> > +
> > +	try_module_get(rcdev->owner);
> 
> What about error-handling here?
>
> I think you want to drop reset_controller_list_mutex only after the call
> to try_module_get()?

I will.

> > +static int devm_reset_control_match(struct device *dev, void *res, void *data)
> > +{
> > +	struct reset_control **rstc = res;
> > +	if (!rstc || !*rstc) {
> > +		WARN_ON(!rstc || !*rstc);
> 
> I think you can if (WARN_ON(...)).

Yes.

> I'm not sure if the error-checks are quite right though;
> reset_control_get always returns an error-pointer for errors, never
> NULL, so the pointer can't ever be NULL. If it somehow was (e.g. client
> usage error), then that NULL pointer would never match anything, so the
> error-check still wouldn't be useful.

The void *res parameter of dr_match_t is given the data field of struct
devres (dr->data) when called by devres_release. A few subsystems do the
same check, so I figured there could be other devres managed resources
that could have dr->data == NULL. Looking at devres.c again, this
doesn't really seem to be the case. I'll drop it.

> I'm not sure why this is a ** here; below in devm_reset_control_put()
> you pass a just a *; are you expected to pass &rstc there instead?

dr->data is filled in and returned by devres_alloc in
devm_reset_control_get and contains a struct reset_control **ptr.
So that should be right, but ...

reset_control_get right now always allocates a new struct reset_control,
instead of keeping them around in a list and just returning a new
reference for already existing reset_controls, which kind of defeats the
purpose of the above. The idea was that drivers sharing a reset line
should also use the same struct reset_control.

thanks
Philipp
Stephen Warren March 4, 2013, 5:03 p.m. UTC | #3
On 03/04/2013 01:33 AM, Philipp Zabel wrote:
> Hi Stephen,
> 
> Am Freitag, den 01.03.2013, 13:00 -0700 schrieb Stephen Warren:
>> On 02/26/2013 04:39 AM, Philipp Zabel wrote:
>>> This adds a simple API for devices to request being reset
>>> by separate reset controller hardware and implements the
>>> reset signal device tree binding.
>>
>>> diff --git a/drivers/reset/core.c b/drivers/reset/core.c
>>
>>> +int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
>>> +			  const struct of_phandle_args *reset_spec, u32 *flags)
>>> +{
>>> +	if (WARN_ON(reset_spec->args_count < rcdev->of_reset_n_cells))
>>> +		return -EINVAL;
>>
>> Would != make more sense than < ?
> 
> I copied this from of_gpio_simple_xlate, because the parsing will work
> if args_count > of_reset_n_cells. In this case the device tree contains
> a #reset-cells larger than what the reset controller driver expects.
> Is this reason enough to assume the whole reset_spec is invalid?

Well, there's certainly something unexpected going on; what are those
extra cells expected to mean? I'd tend to make that an error myself to
avoid unexpected symptoms. I'd tend to want to make the same change to
of_gpio_simple_xlate().

>>> +
>>> +	if (reset_spec->args[0] >= rcdev->nr_resets)
>>> +		return -EINVAL;
>>> +
>>> +	if (flags)
>>> +		*flags = reset_spec->args[1];
>>
>> if (flags)
>>     if (reset_spec->args_count > 1)
>>         *flags = reset_spec->args[1];
>>     else
>>         *flags = 0;
>>
>> ?
> 
> In gpiolib, of_get_named_gpio_flags clears the flags, so that the xlate
> implementations don't have to do it. The core doesn't use the flags, so
> this is not a problem. I don't see the need for
> of_get_named_reset_flags, since the reset consumer drivers shouldn't
> care for those. Maybe it would be better to remove the flags parameter
> from the xlate function altogether.

Yes, since there are no flags, removing the parameter makes most sense.
diff mbox

Patch

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 202fa6d..847f8e3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -162,4 +162,6 @@  source "drivers/irqchip/Kconfig"
 
 source "drivers/ipack/Kconfig"
 
+source "drivers/reset/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 4af933d..682fb7c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -42,6 +42,9 @@  ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
 obj-y				+= clocksource/
 endif
 
+# reset controllers early, since gpu drivers might rely on them to initialize
+obj-$(CONFIG_RESET_CONTROLLER)	+= reset/
+
 # tty/ comes before char/ so that the VT console is the boot-time
 # default.
 obj-y				+= tty/
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
new file mode 100644
index 0000000..c9d04f7
--- /dev/null
+++ b/drivers/reset/Kconfig
@@ -0,0 +1,13 @@ 
+config ARCH_HAS_RESET_CONTROLLER
+	bool
+
+menuconfig RESET_CONTROLLER
+	bool "Reset Controller Support"
+	default y if ARCH_HAS_RESET_CONTROLLER
+	help
+	  Generic Reset Controller support.
+
+	  This framework is designed to abstract reset handling of devices
+	  via GPIOs or SoC-internal reset controller modules.
+
+	  If unsure, say no.
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
new file mode 100644
index 0000000..1e2d83f
--- /dev/null
+++ b/drivers/reset/Makefile
@@ -0,0 +1 @@ 
+obj-$(CONFIG_RESET_CONTROLLER) += core.o
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
new file mode 100644
index 0000000..4e68132
--- /dev/null
+++ b/drivers/reset/core.c
@@ -0,0 +1,298 @@ 
+/*
+ * Reset Controller framework
+ *
+ * Copyright 2013 Philipp Zabel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(reset_controller_list_mutex);
+static LIST_HEAD(reset_controller_list);
+
+/**
+ * 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
+ */
+struct reset_control {
+	struct reset_controller_dev *rcdev;
+	struct device *dev;
+	unsigned int id;
+};
+
+/**
+ * of_reset_simple_xlate - translate reset_spec to the reset line number
+ * @rcdev: a pointer to the reset controller device
+ * @reset_spec: reset line specifier as found in the device tree
+ * @flags: a flags pointer to fill in (optional)
+ *
+ * This simple translation function should be used for reset controllers
+ * with 1:1 mapping, where reset lines can be indexed by number without gaps.
+ */
+int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
+			  const struct of_phandle_args *reset_spec, u32 *flags)
+{
+	if (WARN_ON(reset_spec->args_count < rcdev->of_reset_n_cells))
+		return -EINVAL;
+
+	if (reset_spec->args[0] >= rcdev->nr_resets)
+		return -EINVAL;
+
+	if (flags)
+		*flags = reset_spec->args[1];
+
+	return reset_spec->args[0];
+}
+EXPORT_SYMBOL_GPL(of_reset_simple_xlate);
+
+/**
+ * reset_controller_register - register a reset controller device
+ * @rcdev: a pointer to the initialized reset controller device
+ */
+int reset_controller_register(struct reset_controller_dev *rcdev)
+{
+	if (!rcdev->of_xlate) {
+		rcdev->of_reset_n_cells = 1;
+		rcdev->of_xlate = of_reset_simple_xlate;
+	}
+
+	mutex_lock(&reset_controller_list_mutex);
+	list_add(&rcdev->list, &reset_controller_list);
+	mutex_unlock(&reset_controller_list_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(reset_controller_register);
+
+/**
+ * reset_controller_unregister - unregister a reset controller device
+ * @rcdev: a pointer to the reset controller device
+ */
+void reset_controller_unregister(struct reset_controller_dev *rcdev)
+{
+	mutex_lock(&reset_controller_list_mutex);
+	list_del(&rcdev->list);
+	mutex_unlock(&reset_controller_list_mutex);
+}
+EXPORT_SYMBOL_GPL(reset_controller_unregister);
+
+/**
+ * reset_control_reset - reset the controlled device
+ * @rstc: reset controller
+ */
+int reset_control_reset(struct reset_control *rstc)
+{
+	if (rstc->rcdev->ops->reset)
+		return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
+
+	return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(reset_control_reset);
+
+/**
+ * reset_control_assert - asserts the reset line
+ * @rstc: reset controller
+ */
+int reset_control_assert(struct reset_control *rstc)
+{
+	if (rstc->rcdev->ops->assert)
+		return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id);
+
+	return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(reset_control_assert);
+
+/**
+ * reset_control_deassert - deasserts the reset line
+ * @rstc: reset controller
+ */
+int reset_control_deassert(struct reset_control *rstc)
+{
+	if (rstc->rcdev->ops->deassert)
+		return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id);
+
+	return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(reset_control_deassert);
+
+/**
+ * reset_control_get - Lookup and obtain a 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.
+ *
+ * Use of id names is optional.
+ */
+struct reset_control *reset_control_get(struct device *dev, const char *id)
+{
+	struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER);
+	struct reset_controller_dev *r, *rcdev;
+	struct of_phandle_args args;
+	int index = 0;
+	int rstc_id;
+	int ret;
+
+	if (!dev)
+		return ERR_PTR(-EINVAL);
+
+	if (id)
+		index = of_property_match_string(dev->of_node,
+						 "reset-names", id);
+	ret = of_parse_phandle_with_args(dev->of_node, "resets", "#reset-cells",
+					 index, &args);
+	if (ret)
+		return ERR_PTR(ret);
+
+	mutex_lock(&reset_controller_list_mutex);
+	rcdev = NULL;
+	list_for_each_entry(r, &reset_controller_list, list) {
+		if (args.np == r->of_node) {
+			rcdev = r;
+			break;
+		}
+	}
+	mutex_unlock(&reset_controller_list_mutex);
+	of_node_put(args.np);
+
+	if (!rcdev)
+		return ERR_PTR(-ENODEV);
+
+	rstc_id = rcdev->of_xlate(rcdev, &args, NULL);
+	if (rstc_id < 0)
+		return ERR_PTR(rstc_id);
+
+	try_module_get(rcdev->owner);
+
+	rstc = kzalloc(sizeof(*rstc), GFP_KERNEL);
+	if (!rstc) {
+		module_put(rstc->rcdev->owner);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	rstc->dev = dev;
+	rstc->rcdev = rcdev;
+	rstc->id = rstc_id;
+
+	return rstc;
+}
+EXPORT_SYMBOL_GPL(reset_control_get);
+
+/**
+ * reset_control_put - free the reset controller
+ * @rstc: reset controller
+ */
+
+void reset_control_put(struct reset_control *rstc)
+{
+	if (IS_ERR(rstc))
+		return;
+
+	module_put(rstc->rcdev->owner);
+	kfree(rstc);
+}
+EXPORT_SYMBOL_GPL(reset_control_put);
+
+static void devm_reset_control_release(struct device *dev, void *res)
+{
+	reset_control_put(*(struct reset_control **)res);
+}
+
+/**
+ * devm_reset_control_get - resource managed reset_control_get()
+ * @dev: device to be reset by the controller
+ * @id: reset line name
+ *
+ * Managed reset_control_get(). For reset controllers returned from this
+ * function, reset_control_put() is called automatically on driver detach.
+ * See reset_control_get() for more information.
+ */
+struct reset_control *devm_reset_control_get(struct device *dev, const char *id)
+{
+	struct reset_control **ptr, *rstc;
+
+	ptr = devres_alloc(devm_reset_control_release, sizeof(*ptr),
+			   GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	rstc = reset_control_get(dev, id);
+	if (!IS_ERR(rstc)) {
+		*ptr = rstc;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return rstc;
+}
+EXPORT_SYMBOL_GPL(devm_reset_control_get);
+
+static int devm_reset_control_match(struct device *dev, void *res, void *data)
+{
+	struct reset_control **rstc = res;
+	if (!rstc || !*rstc) {
+		WARN_ON(!rstc || !*rstc);
+		return 0;
+	}
+	return *rstc == data;
+}
+
+/**
+ * devm_reset_control_put - resource managed reset_control_put()
+ * @rstc: reset controller to free
+ *
+ * Deallocate a reset control allocated withd devm_reset_control_get().
+ * This function will not need to be called normally, as devres will take
+ * care of freeing the resource.
+ */
+void devm_reset_control_put(struct reset_control *rstc)
+{
+	int ret;
+
+	ret = devres_release(rstc->dev, devm_reset_control_release,
+			     devm_reset_control_match, rstc);
+	if (ret)
+		WARN_ON(ret);
+}
+EXPORT_SYMBOL_GPL(devm_reset_control_put);
+
+/**
+ * device_reset - find reset controller associated with the device
+ *                and perform reset
+ * @dev: device to be reset by the controller
+ *
+ * Convenience wrapper for reset_control_get() and reset_control_reset().
+ * This is useful for the common case of devices with single, dedicated reset
+ * lines.
+ */
+int device_reset(struct device *dev)
+{
+	struct reset_control *rstc;
+	int ret;
+
+	rstc = reset_control_get(dev, NULL);
+	if (IS_ERR(rstc))
+		return PTR_ERR(rstc);
+
+	ret = reset_control_reset(rstc);
+
+	reset_control_put(rstc);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(device_reset);
diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h
new file mode 100644
index 0000000..a077f9e
--- /dev/null
+++ b/include/linux/reset-controller.h
@@ -0,0 +1,51 @@ 
+#ifndef _LINUX_RESET_CONTROLLER_H_
+#define _LINUX_RESET_CONTROLLER_H_
+
+#include <linux/list.h>
+
+struct reset_controller_dev;
+
+/**
+ * struct reset_control_ops
+ *
+ * @reset: for self-deasserting resets, does all necessary
+ *         things to reset the device
+ * @assert: manually assert the reset line, if supported
+ * @deassert: manually deassert the reset line, if supported
+ */
+struct reset_control_ops {
+	int (*reset)(struct reset_controller_dev *rcdev, unsigned long id);
+	int (*assert)(struct reset_controller_dev *rcdev, unsigned long id);
+	int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id);
+};
+
+struct module;
+struct device_node;
+
+/**
+ * struct reset_controller_dev - reset controller entity that might
+ *                               provide multiple reset controls
+ * @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
+ * @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
+ *            device tree to id as given to the reset control ops
+ * @nr_resets: number of reset controls in this reset controller device
+ */
+struct reset_controller_dev {
+	struct reset_control_ops *ops;
+	struct module *owner;
+	struct list_head list;
+	struct device_node *of_node;
+	int of_reset_n_cells;
+	int (*of_xlate)(struct reset_controller_dev *rcdev,
+			const struct of_phandle_args *reset_spec, u32 *flags);
+	unsigned int nr_resets;
+};
+
+int reset_controller_register(struct reset_controller_dev *rcdev);
+void reset_controller_unregister(struct reset_controller_dev *rcdev);
+
+#endif
diff --git a/include/linux/reset.h b/include/linux/reset.h
new file mode 100644
index 0000000..6082247
--- /dev/null
+++ b/include/linux/reset.h
@@ -0,0 +1,17 @@ 
+#ifndef _LINUX_RESET_H_
+#define _LINUX_RESET_H_
+
+struct device;
+struct reset_control;
+
+int reset_control_reset(struct reset_control *rstc);
+int reset_control_assert(struct reset_control *rstc);
+int reset_control_deassert(struct reset_control *rstc);
+
+struct reset_control *reset_control_get(struct device *dev, const char *id);
+void reset_control_put(struct reset_control *rstc);
+struct reset_control *devm_reset_control_get(struct device *dev, const char *id);
+
+int device_reset(struct device *dev);
+
+#endif