* [PATCH v4 1/4] nvmem: fix memory leak in error path
From: Bartosz Golaszewski @ 2020-02-20 10:01 UTC (permalink / raw)
To: Linus Walleij, Srinivas Kandagatla, Khouloud Touil,
Geert Uytterhoeven
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
In-Reply-To: <20200220100141.5905-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
We need to free the ida mapping and nvmem struct if the write-protect
GPIO lookup fails.
Fixes: 2a127da461a9 ("nvmem: add support for the write-protect pin")
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/nvmem/core.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 503da67dde06..948c7ebce340 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -353,8 +353,11 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
else
nvmem->wp_gpio = gpiod_get_optional(config->dev, "wp",
GPIOD_OUT_HIGH);
- if (IS_ERR(nvmem->wp_gpio))
+ if (IS_ERR(nvmem->wp_gpio)) {
+ ida_simple_remove(&nvmem_ida, nvmem->id);
+ kfree(nvmem);
return ERR_CAST(nvmem->wp_gpio);
+ }
kref_init(&nvmem->refcnt);
INIT_LIST_HEAD(&nvmem->cells);
--
2.25.0
^ permalink raw reply related
* [PATCH v4 0/4] nvmem/gpio: fix resource management
From: Bartosz Golaszewski @ 2020-02-20 10:01 UTC (permalink / raw)
To: Linus Walleij, Srinivas Kandagatla, Khouloud Touil,
Geert Uytterhoeven
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This series addresses a couple problems with memory management in nvmem
core.
First we fix a memory leak introduced in this release cycle. Next we extend
the GPIO framework to use reference counting for GPIO descriptors. We then
use it to fix the resource management problem with the write-protect pin.
Finally we add some readability tweaks and a comment clearing up some
confusion about resource management.
While the memory leak with wp-gpios is now in mainline - I'm not sure how
to go about applying the kref patch. This is theoretically a new feature
but it's also the cleanest way of fixing the problem.
v1 -> v2:
- make gpiod_ref() helper return
- reorganize the series for easier merging
- fix another memory leak
v2 -> v3:
- drop incorrect patches
- add a patch adding a comment about resource management
- extend the GPIO kref patch: only increment the reference count if the
descriptor is associated with a requested line
v3 -> v4:
- fixed the return value in error path in nvmem_register()
- dropped patches already applied to the nvmem tree
- dropped the patch adding the comment about resource management
Bartosz Golaszewski (3):
nvmem: fix memory leak in error path
gpiolib: use kref in gpio_desc
nvmem: increase the reference count of a gpio passed over config
Khouloud Touil (1):
nvmem: release the write-protect pin
drivers/gpio/gpiolib.c | 36 ++++++++++++++++++++++++++++++++---
drivers/gpio/gpiolib.h | 1 +
drivers/nvmem/core.c | 8 ++++++--
include/linux/gpio/consumer.h | 1 +
4 files changed, 41 insertions(+), 5 deletions(-)
--
2.25.0
^ permalink raw reply
* [PATCH v4 3/4] nvmem: increase the reference count of a gpio passed over config
From: Bartosz Golaszewski @ 2020-02-20 10:01 UTC (permalink / raw)
To: Linus Walleij, Srinivas Kandagatla, Khouloud Touil,
Geert Uytterhoeven
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
In-Reply-To: <20200220100141.5905-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
We can obtain the write-protect GPIO in nvmem_register() by requesting
it ourselves or by storing the gpio_desc passed in nvmem_config. In the
latter case we need to increase the reference count so that it gets
freed correctly.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/nvmem/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 948c7ebce340..071fb897394c 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -349,7 +349,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
}
if (config->wp_gpio)
- nvmem->wp_gpio = config->wp_gpio;
+ nvmem->wp_gpio = gpiod_ref(config->wp_gpio);
else
nvmem->wp_gpio = gpiod_get_optional(config->dev, "wp",
GPIOD_OUT_HIGH);
--
2.25.0
^ permalink raw reply related
* [PATCH v4 2/4] gpiolib: use kref in gpio_desc
From: Bartosz Golaszewski @ 2020-02-20 10:01 UTC (permalink / raw)
To: Linus Walleij, Srinivas Kandagatla, Khouloud Touil,
Geert Uytterhoeven
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
In-Reply-To: <20200220100141.5905-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
GPIO descriptors are freed by consumers using gpiod_put(). The name of
this function suggests some reference counting is going on but it's not
true.
Use kref to actually introduce reference counting for gpio_desc objects.
Add a corresponding gpiod_get() helper for increasing the reference count.
This doesn't change anything for already existing (correct) drivers but
allows us to keep track of GPIO descs used by multiple users.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/gpio/gpiolib.c | 36 ++++++++++++++++++++++++++++++++---
drivers/gpio/gpiolib.h | 1 +
include/linux/gpio/consumer.h | 1 +
3 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 753283486037..42f820356279 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2798,6 +2798,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
goto done;
}
+ kref_init(&desc->ref);
+
if (chip->request) {
/* chip->request may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
@@ -2933,6 +2935,13 @@ void gpiod_free(struct gpio_desc *desc)
}
}
+static void gpiod_free_kref(struct kref *ref)
+{
+ struct gpio_desc *desc = container_of(ref, struct gpio_desc, ref);
+
+ gpiod_free(desc);
+}
+
/**
* gpiochip_is_requested - return string iff signal was requested
* @chip: controller managing the signal
@@ -5047,18 +5056,39 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev,
EXPORT_SYMBOL_GPL(gpiod_get_array_optional);
/**
- * gpiod_put - dispose of a GPIO descriptor
- * @desc: GPIO descriptor to dispose of
+ * gpiod_put - decrease the reference count of a GPIO descriptor
+ * @desc: GPIO descriptor to unref
*
* No descriptor can be used after gpiod_put() has been called on it.
*/
void gpiod_put(struct gpio_desc *desc)
{
if (desc)
- gpiod_free(desc);
+ kref_put(&desc->ref, gpiod_free_kref);
}
EXPORT_SYMBOL_GPL(gpiod_put);
+/**
+ * gpiod_ref - increase the reference count of a GPIO descriptor
+ * @desc: GPIO descriptor to reference
+ *
+ * Returns the same gpio_desc after increasing the reference count.
+ */
+struct gpio_desc *gpiod_ref(struct gpio_desc *desc)
+{
+ if (!desc)
+ return NULL;
+
+ if (!test_bit(FLAG_REQUESTED, &desc->flags)) {
+ pr_warn("gpiolib: unable to increase the reference count of unrequested GPIO descriptor\n");
+ return desc;
+ }
+
+ kref_get(&desc->ref);
+ return desc;
+}
+EXPORT_SYMBOL_GPL(gpiod_ref);
+
/**
* gpiod_put_array - dispose of multiple GPIO descriptors
* @descs: struct gpio_descs containing an array of descriptors
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 3e0aab2945d8..51a92c43dd55 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -119,6 +119,7 @@ struct gpio_desc {
const char *label;
/* Name of the GPIO */
const char *name;
+ struct kref ref;
};
int gpiod_request(struct gpio_desc *desc, const char *label);
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index bf2d017dd7b7..c7b5fb3d9d64 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -81,6 +81,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags);
+struct gpio_desc *gpiod_ref(struct gpio_desc *desc);
void gpiod_put(struct gpio_desc *desc);
void gpiod_put_array(struct gpio_descs *descs);
--
2.25.0
^ permalink raw reply related
* [PATCH v4 4/4] nvmem: release the write-protect pin
From: Bartosz Golaszewski @ 2020-02-20 10:01 UTC (permalink / raw)
To: Linus Walleij, Srinivas Kandagatla, Khouloud Touil,
Geert Uytterhoeven
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
In-Reply-To: <20200220100141.5905-1-brgl@bgdev.pl>
From: Khouloud Touil <ktouil@baylibre.com>
Put the write-protect GPIO descriptor in nvmem_release() so that it can
be automatically released when the associated device's reference count
drops to 0.
Fixes: 2a127da461a9 ("nvmem: add support for the write-protect pin")
Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Khouloud Touil <ktouil@baylibre.com>
[Bartosz: tweak the commit message]
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/nvmem/core.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 071fb897394c..a82375f63d64 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -72,6 +72,7 @@ static void nvmem_release(struct device *dev)
struct nvmem_device *nvmem = to_nvmem_device(dev);
ida_simple_remove(&nvmem_ida, nvmem->id);
+ gpiod_put(nvmem->wp_gpio);
kfree(nvmem);
}
--
2.25.0
^ permalink raw reply related
* Re: [PATCH] lttng-modules: Check the pid_ns before using it because it may be NULL
From: Richard Purdie @ 2020-02-20 10:02 UTC (permalink / raw)
To: Li Zhou, openembedded-core
In-Reply-To: <1582165584-187223-1-git-send-email-li.zhou@windriver.com>
On Thu, 2020-02-20 at 10:26 +0800, Li Zhou wrote:
> Check the pid_ns before using it because it may be NULL to fix below
> issue:
> <1>[ 22.637196] Unable to handle kernel NULL pointer dereference at
> virtual address 0000000000000080
> <1>[ 22.645982] Mem abort info:
> <1>[ 22.648769] ESR = 0x96000007
> <1>[ 22.651817] Exception class = DABT (current EL), IL = 32 bits
> <1>[ 22.657730] SET = 0, FnV = 0
> <1>[ 22.660777] EA = 0, S1PTW = 0
> <1>[ 22.663910] Data abort info:
> <1>[ 22.666784] ISV = 0, ISS = 0x00000007
> <1>[ 22.670611] CM = 0, WnR = 0
> <1>[ 22.673574] user pgtable: 4k pages, 39-bit VAs, pgdp =
> 0000000012378f78
> <1>[ 22.680180] [0000000000000080] pgd=000000007f023003,
> pud=000000007f023003, pmd=000000007f01f003, pte=0000000000000000
> <0>[ 22.690794] Internal error: Oops: 96000007 [#1] PREEMPT SMP
> <4>[ 22.690797] Modules linked in: adkNetD ncp
> lttng_ring_buffer_client_overwrite(C)
> lttng_ring_buffer_metadata_client(C)
> lttng_ring_buffer_client_discard(C)
> lttng_ring_buffer_client_mmap_overwrite(C)
> lttng_ring_buffer_client_mmap_discard(C)
> lttng_ring_buffer_metadata_mmap_client(C) lttng_probe_signal(C)
> lttng_probe_printk(C) lttng_probe_sched(C) lttng_probe_irq(C)
> lttng_tracer(C) lttng_statedump(C) lttng_ftrace(C)
> lttng_lib_ring_buffer(C) lttng_clock_plugin_arm_cntpct(C)
> lttng_clock(C)
> <0>[ 22.690823] Process lttng-sessiond (pid: 3093, stack limit =
> 0x000000005d27910f)
> <4>[ 22.690828] CPU: 1 PID: 3093 Comm: lttng-sessiond Tainted: G C
> 4.18.37-rt820-custom #1
> <4>[ 22.690830] Hardware name: DUS33 (CPM2-20) (DT)
> <4>[ 22.690833] pstate: 60000005 (nZCv daif -PAN -UAO)
> <4>[ 22.690845] pc : do_lttng_statedump+0xcc/0x8a8 [lttng_statedump]
> <4>[ 22.690849] lr : do_lttng_statedump+0xc4/0x8a8 [lttng_statedump]
> <4>[ 22.690851] sp : ffffffc07fe57ad0
> <4>[ 22.690852] x29: ffffffc07fe57ad0 x28: ffffffc008ae2700
> <4>[ 22.690856] x27: ffffff8000724000 x26: 0000000000000001
> <4>[ 22.690859] x25: ffffff80089c9620 x24: 0000000000000000
> <4>[ 22.690862] x23: ffffffc008ae2e10 x22: ffffff80089d3380
> <4>[ 22.690865] x21: ffffffc07f450000 x20: ffffffc008ae2700
> <4>[ 22.690869] x19: 0000000000000007 x18: 00000000fffffffe
> <4>[ 22.690871] x17: 0000000000000000 x16: ffffff800824b980
> <4>[ 22.690874] x15: 0000000000000000 x14: 736162203b656e6f
> <4>[ 22.690877] x13: 6e203d20676e6964 x12: 0000000000000000
> <4>[ 22.690880] x11: 0101010101010101 x10: 7f7f7f7f7f7f7f7f
> <4>[ 22.690882] x9 : 3c1f647968721eff x8 : ffffffc0877504c8
> <4>[ 22.690886] x7 : 09093a7c093a7c08 x6 : ffffff8010c4b317
> <4>[ 22.690888] x5 : 0000000000000000 x4 : 00000040a7575000
> <4>[ 22.690891] x3 : ffffffc008ae2e28 x2 : 0000000000000000
> <4>[ 22.690894] x1 : 0000000000000000 x0 : 0000000000000000
> <4>[ 22.690896] Call trace:
> <4>[ 22.690902] do_lttng_statedump+0xcc/0x8a8 [lttng_statedump]
> <4>[ 22.690905] lttng_statedump_start+0x20/0x30 [lttng_statedump]
> <4>[ 22.690981] lttng_session_enable+0xf0/0x120 [lttng_tracer]
> <4>[ 22.691018] lttng_session_ioctl+0x22c/0x328 [lttng_tracer]
> <4>[ 22.691026] compat_sys_ioctl+0x110/0x778
>
> Signed-off-by: Li Zhou <li.zhou@windriver.com>
Are upstream aware of this issue? I'd really like their opinion on this
before we merge anything.
Cheers,
Richard
^ permalink raw reply
* Re: [PATCH net-next v3 6/9] drivers/base/power: add dpm_sysfs_change_owner()
From: Rafael J. Wysocki @ 2020-02-20 10:02 UTC (permalink / raw)
To: Christian Brauner
Cc: David S. Miller, Greg Kroah-Hartman, Linux Kernel Mailing List,
netdev, Rafael J. Wysocki, Pavel Machek, Jakub Kicinski,
Eric Dumazet, Stephen Hemminger, Linux PM
In-Reply-To: <20200218162943.2488012-7-christian.brauner@ubuntu.com>
On Tue, Feb 18, 2020 at 5:30 PM Christian Brauner
<christian.brauner@ubuntu.com> wrote:
>
> Add a helper to change the owner of a device's power entries. This
> needs to happen when the ownership of a device is changed, e.g. when
> moving network devices between network namespaces.
> This function will be used to correctly account for ownership changes,
> e.g. when moving network devices between network namespaces.
>
> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
> ---
> /* v2 */
> - "Rafael J. Wysocki" <rafael@kernel.org>:
> - Fold if (dev->power.wakeup && dev->power.wakeup->dev) check into
> if (device_can_wakeup(dev)) check since the former can never be true if
> the latter is false.
>
> - Christian Brauner <christian.brauner@ubuntu.com>:
> - Place (dev->power.wakeup && dev->power.wakeup->dev) check under
> CONFIG_PM_SLEEP ifdefine since it will wakeup_source will only be available
> when this config option is set.
>
> /* v3 */
> - Greg Kroah-Hartman <gregkh@linuxfoundation.org>:
> - Add explicit uid/gid parameters.
> ---
> drivers/base/core.c | 4 ++++
> drivers/base/power/power.h | 3 +++
> drivers/base/power/sysfs.c | 42 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 49 insertions(+)
>
> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index ec0d5e8cfd0f..efec2792f5d7 100644
> --- a/drivers/base/core.c
> +++ b/drivers/base/core.c
> @@ -3522,6 +3522,10 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
> if (error)
> goto out;
>
> + error = dpm_sysfs_change_owner(dev, kuid, kgid);
> + if (error)
> + goto out;
> +
> #ifdef CONFIG_BLOCK
> if (sysfs_deprecated && dev->class == &block_class)
> goto out;
> diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
> index 444f5c169a0b..54292cdd7808 100644
> --- a/drivers/base/power/power.h
> +++ b/drivers/base/power/power.h
> @@ -74,6 +74,7 @@ extern int pm_qos_sysfs_add_flags(struct device *dev);
> extern void pm_qos_sysfs_remove_flags(struct device *dev);
> extern int pm_qos_sysfs_add_latency_tolerance(struct device *dev);
> extern void pm_qos_sysfs_remove_latency_tolerance(struct device *dev);
> +extern int dpm_sysfs_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid);
>
> #else /* CONFIG_PM */
>
> @@ -88,6 +89,8 @@ static inline void pm_runtime_remove(struct device *dev) {}
>
> static inline int dpm_sysfs_add(struct device *dev) { return 0; }
> static inline void dpm_sysfs_remove(struct device *dev) {}
> +static inline int dpm_sysfs_change_owner(struct device *dev, kuid_t kuid,
> + kgid_t kgid) { return 0; }
>
> #endif
>
> diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
> index d7d82db2e4bc..4e79afcd5ca8 100644
> --- a/drivers/base/power/sysfs.c
> +++ b/drivers/base/power/sysfs.c
> @@ -684,6 +684,48 @@ int dpm_sysfs_add(struct device *dev)
> return rc;
> }
>
> +int dpm_sysfs_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
> +{
> + int rc;
> +
> + if (device_pm_not_required(dev))
> + return 0;
> +
> + rc = sysfs_group_change_owner(&dev->kobj, &pm_attr_group, kuid, kgid);
> + if (rc)
> + return rc;
> +
> + if (pm_runtime_callbacks_present(dev)) {
> + rc = sysfs_group_change_owner(
> + &dev->kobj, &pm_runtime_attr_group, kuid, kgid);
> + if (rc)
> + return rc;
> + }
> + if (device_can_wakeup(dev)) {
> + rc = sysfs_group_change_owner(&dev->kobj, &pm_wakeup_attr_group,
> + kuid, kgid);
> + if (rc)
> + return rc;
> +
> +#ifdef CONFIG_PM_SLEEP
> + if (dev->power.wakeup && dev->power.wakeup->dev) {
> + rc = device_change_owner(dev->power.wakeup->dev, kuid,
> + kgid);
> + if (rc)
> + return rc;
> + }
> +#endif
First off, I don't particularly like #ifdefs in function bodies. In
particular, there is a CONFIG_PM_SLEEP block in this file already and
you could define a new function in there to carry out the above
operations, and provide an empty stub of it for the "unset" case.
Failing to do so is somewhat on the "rushing things in" side in my
view.
Second, the #ifdef should cover the entire if (device_can_wakeup(dev))
{} block, because wakeup_sysfs_add() is only called if
device_can_wakeup(dev) returns 'true' for the device in question (and
arguably you could have checked that easily enough).
> + }
> + if (dev->power.set_latency_tolerance) {
> + rc = sysfs_group_change_owner(
> + &dev->kobj, &pm_qos_latency_tolerance_attr_group, kuid,
> + kgid);
> + if (rc)
> + return rc;
> + }
> + return 0;
> +}
> +
> int wakeup_sysfs_add(struct device *dev)
> {
> return sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);
> --
> 2.25.0
>
^ permalink raw reply
* Re: [dpdk-dev] CONFIG_RTE_MAX_MEM_MB fails in DPDK18.05
From: Burakov, Anatoly @ 2020-02-20 10:02 UTC (permalink / raw)
To: Kamaraj P
Cc: Kevin Traynor, dev, Nageswara Rao Penumarthy, Kamaraj P (kamp),
mtang2
In-Reply-To: <CAG8PAaowDOvWkeA0FUbUHz4UB85afhwnT7Wca0Asq4Q2YDeE-g@mail.gmail.com>
On 19-Feb-20 4:20 PM, Kamaraj P wrote:
> Hi Anatoly,
>
> Yes we are facing an issue with our custom applications.
> Earlier we have tried with l2fwd DPDK application and does not see any
> issue with memory initialization.
> Not sure whether we missed any other options.
>
> BTW when we tried with l2fwd application, the application does not seem
> to hang during the memory initialization where as our application is
> kind of struck when getting memory.
> Do we tune any config parameters from DPDK ?
> Please advise.
>
Hi,
This doesn't look like an issue that is with DPDK. It is more likely
that something else triggers this (similar to mlockall()). Are you sure
there are no more mem lock calls anywhere in your application before EAL
init (i.e. inside the libraries you use, etc.)? Because so far, page
pinning is the only thing i can think of that would cause this sort of
behavior.
--
Thanks,
Anatoly
^ permalink raw reply
* [PATCH 1/6] regulator: import Linux regulator_bulk API
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum, mgr, rcz
In-Reply-To: <20200220100109.18147-1-a.fatoum@pengutronix.de>
Linux v5.6-rc1 contains 168 references to regultor_bulk_get, which
allows getting multiple regulators to set at once. Instead of open
coding them when porting code, port over the helpers to barebox.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/regulator/core.c | 139 +++++++++++++++++++++++++++++++++++++++
include/regulator.h | 49 ++++++++++++++
2 files changed, 188 insertions(+)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index f0de7a52e391..f459d072a987 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -397,6 +397,145 @@ int regulator_set_voltage(struct regulator *r, int min_uV, int max_uV)
return regulator_set_voltage_internal(r->ri, min_uV, max_uV);
}
+/**
+ * regulator_bulk_get - get multiple regulator consumers
+ *
+ * @dev: Device to supply
+ * @num_consumers: Number of consumers to register
+ * @consumers: Configuration of consumers; clients are stored here.
+ *
+ * @return 0 on success, an errno on failure.
+ *
+ * This helper function allows drivers to get several regulator
+ * consumers in one operation. If any of the regulators cannot be
+ * acquired then any regulators that were allocated will be freed
+ * before returning to the caller.
+ */
+int regulator_bulk_get(struct device_d *dev, int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < num_consumers; i++)
+ consumers[i].consumer = NULL;
+
+ for (i = 0; i < num_consumers; i++) {
+ consumers[i].consumer = regulator_get(dev,
+ consumers[i].supply);
+ if (IS_ERR(consumers[i].consumer)) {
+ ret = PTR_ERR(consumers[i].consumer);
+ consumers[i].consumer = NULL;
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get supply '%s': %d\n",
+ consumers[i].supply, ret);
+ else
+ dev_dbg(dev, "Failed to get supply '%s', deferring\n",
+ consumers[i].supply);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_get);
+
+/**
+ * regulator_bulk_enable - enable multiple regulator consumers
+ *
+ * @num_consumers: Number of consumers
+ * @consumers: Consumer data; clients are stored here.
+ * @return 0 on success, an errno on failure
+ *
+ * This convenience API allows consumers to enable multiple regulator
+ * clients in a single API call. If any consumers cannot be enabled
+ * then any others that were enabled will be disabled again prior to
+ * return.
+ */
+int regulator_bulk_enable(int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < num_consumers; i++) {
+ ret = regulator_enable(consumers[i].consumer);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (--i >= 0)
+ regulator_disable(consumers[i].consumer);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_enable);
+
+/**
+ * regulator_bulk_disable - disable multiple regulator consumers
+ *
+ * @num_consumers: Number of consumers
+ * @consumers: Consumer data; clients are stored here.
+ * @return 0 on success, an errno on failure
+ *
+ * This convenience API allows consumers to disable multiple regulator
+ * clients in a single API call. If any consumers cannot be disabled
+ * then any others that were disabled will be enabled again prior to
+ * return.
+ */
+int regulator_bulk_disable(int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ int i;
+ int ret, r;
+
+ for (i = num_consumers - 1; i >= 0; --i) {
+ ret = regulator_disable(consumers[i].consumer);
+ if (ret != 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pr_err("Failed to disable %s: %d\n", consumers[i].supply, ret);
+ for (++i; i < num_consumers; ++i) {
+ r = regulator_enable(consumers[i].consumer);
+ if (r != 0)
+ pr_err("Failed to re-enable %s: %d\n",
+ consumers[i].supply, r);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_disable);
+
+/**
+ * regulator_bulk_free - free multiple regulator consumers
+ *
+ * @num_consumers: Number of consumers
+ * @consumers: Consumer data; clients are stored here.
+ *
+ * This convenience API allows consumers to free multiple regulator
+ * clients in a single API call.
+ */
+void regulator_bulk_free(int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ int i;
+
+ for (i = 0; i < num_consumers; i++)
+ consumers[i].consumer = NULL;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_free);
+
static void regulator_print_one(struct regulator_internal *ri)
{
struct regulator *r;
diff --git a/include/regulator.h b/include/regulator.h
index d01535df5207..dfa808d662bd 100644
--- a/include/regulator.h
+++ b/include/regulator.h
@@ -5,6 +5,23 @@
/* struct regulator is an opaque object for consumers */
struct regulator;
+/**
+ * struct regulator_bulk_data - Data used for bulk regulator operations.
+ *
+ * @supply: The name of the supply. Initialised by the user before
+ * using the bulk regulator APIs.
+ * @consumer: The regulator consumer for the supply. This will be managed
+ * by the bulk API.
+ *
+ * The regulator APIs provide a series of regulator_bulk_() API calls as
+ * a convenience to consumers which require multiple supplies. This
+ * structure is used to manage data for these calls.
+ */
+struct regulator_bulk_data {
+ const char *supply;
+ struct regulator *consumer;
+};
+
/**
* struct regulator_desc - Static regulator descriptor
*
@@ -136,6 +153,14 @@ int regulator_list_voltage_linear_range(struct regulator_dev *rdev,
int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev);
int regulator_map_voltage_iterate(struct regulator_dev *rdev,
int min_uV, int max_uV);
+int regulator_bulk_get(struct device_d *dev, int num_consumers,
+ struct regulator_bulk_data *consumers);
+int regulator_bulk_enable(int num_consumers,
+ struct regulator_bulk_data *consumers);
+int regulator_bulk_disable(int num_consumers,
+ struct regulator_bulk_data *consumers);
+void regulator_bulk_free(int num_consumers,
+ struct regulator_bulk_data *consumers);
/*
* Helper functions intended to be used by regulator drivers prior registering
@@ -166,6 +191,30 @@ static inline int regulator_set_voltage(struct regulator *regulator,
return 0;
}
+static inline int regulator_bulk_get(struct device_d *dev, int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ return 0;
+}
+
+static inline int regulator_bulk_enable(int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ return 0;
+}
+
+static inline int regulator_bulk_disable(int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ return 0;
+}
+
+static inline void regulator_bulk_free(int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ return 0;
+}
+
#endif
#endif /* __REGULATOR_H */
--
2.25.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 0/6] phy: usb: add support for STM32 usbphyc
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum, mgr, rcz
This is only USB PHY support. Changes to the DWC2 (OTG) and/or OHCI
(Host) are still needed to make USB on the MP1 work.
I haven't yet groked the relation between generic phy and usb phy
frameworks, but the code is a straight port from Linux and it seems to
work[1], so let's upstream it now to make collaboration easier.
[1]: clock the EHCI and power on the PHY and 20% of the time, the bus
can be enumerated completely. Still needs further debugging,
but it's unlikely that the PHY driver is at fault.
Cheers,
Ahmad Fatoum (6):
regulator: import Linux regulator_bulk API
phy: remove unused init_data parameter
phy: populate existing ->pwr member with phy-supply
phy: introduce phy_get_by_index
regulator: port over Linux stm32 PWR regulator driver
phy: port over Linux stm32 usbphyc driver
drivers/phy/Kconfig | 13 +
drivers/phy/Makefile | 1 +
drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 2 +-
drivers/phy/phy-core.c | 29 +-
drivers/phy/phy-stm32-usbphyc.c | 434 +++++++++++++++++++++
drivers/phy/usb-nop-xceiv.c | 2 +-
drivers/pinctrl/pinctrl-tegra-xusb.c | 4 +-
drivers/regulator/Kconfig | 7 +
drivers/regulator/Makefile | 1 +
drivers/regulator/core.c | 139 +++++++
drivers/regulator/stm32-pwr.c | 215 ++++++++++
drivers/usb/imx/imx-usb-phy.c | 2 +-
include/linux/phy/phy.h | 14 +-
include/regulator.h | 49 +++
14 files changed, 897 insertions(+), 15 deletions(-)
create mode 100644 drivers/phy/phy-stm32-usbphyc.c
create mode 100644 drivers/regulator/stm32-pwr.c
--
2.25.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply
* [PATCH 5/6] regulator: port over Linux stm32 PWR regulator driver
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum, mgr, rcz
In-Reply-To: <20200220100109.18147-1-a.fatoum@pengutronix.de>
This driver supports internal regulators (1V1, 1V8, 3V3) in the
STMicroelectronics STM32 chips. Control of these regulators will
be required when adding USB support later on.
Imported here is the Linux v5.6-rc1 state of the driver.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/regulator/Kconfig | 7 ++
drivers/regulator/Makefile | 1 +
drivers/regulator/stm32-pwr.c | 215 ++++++++++++++++++++++++++++++++++
3 files changed, 223 insertions(+)
create mode 100644 drivers/regulator/stm32-pwr.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index f47a115da240..1ce057180a01 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -21,6 +21,13 @@ config REGULATOR_PFUZE
depends on I2C
depends on ARCH_IMX6 || ARCH_IMX8MQ
+config REGULATOR_STM32_PWR
+ bool "STMicroelectronics STM32 PWR"
+ depends on ARCH_STM32MP
+ help
+ This driver supports internal regulators (1V1, 1V8, 3V3) in the
+ STMicroelectronics STM32 chips.
+
config REGULATOR_STPMIC1
tristate "STMicroelectronics STPMIC1 PMIC Regulators"
depends on MFD_STPMIC1
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index e27e155cf624..4d0bba6c52dd 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_REGULATOR_BCM283X) += bcm2835.o
obj-$(CONFIG_REGULATOR_PFUZE) += pfuze.o
obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
+obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o
diff --git a/drivers/regulator/stm32-pwr.c b/drivers/regulator/stm32-pwr.c
new file mode 100644
index 000000000000..296f95bc4c32
--- /dev/null
+++ b/drivers/regulator/stm32-pwr.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2019
+// Authors: Gabriel Fernandez <gabriel.fernandez@st.com>
+// Pascal Paillet <p.paillet@st.com>.
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <linux/iopoll.h>
+#include <of.h>
+#include <regulator.h>
+
+/*
+ * Registers description
+ */
+#define REG_PWR_CR3 0x0C
+
+#define USB_3_3_EN BIT(24)
+#define USB_3_3_RDY BIT(26)
+#define REG_1_8_EN BIT(28)
+#define REG_1_8_RDY BIT(29)
+#define REG_1_1_EN BIT(30)
+#define REG_1_1_RDY BIT(31)
+
+/* list of supported regulators */
+enum {
+ PWR_REG11,
+ PWR_REG18,
+ PWR_USB33,
+ STM32PWR_REG_NUM_REGS
+};
+
+struct stm32_pwr_desc {
+ struct regulator_desc desc;
+ const char *name;
+ const char *supply_name;
+};
+
+static u32 ready_mask_table[STM32PWR_REG_NUM_REGS] = {
+ [PWR_REG11] = REG_1_1_RDY,
+ [PWR_REG18] = REG_1_8_RDY,
+ [PWR_USB33] = USB_3_3_RDY,
+};
+
+struct stm32_pwr_reg {
+ void __iomem *base;
+ struct device_d *dev;
+ u32 ready_mask;
+ struct regulator_dev rdev;
+ struct regulator *supply;
+};
+
+static inline struct stm32_pwr_reg *to_pwr_reg(struct regulator_dev *rdev)
+{
+ return container_of(rdev, struct stm32_pwr_reg, rdev);
+}
+
+static inline struct stm32_pwr_desc *to_desc(struct regulator_dev *rdev)
+{
+ return container_of(rdev->desc, struct stm32_pwr_desc, desc);
+}
+
+static int stm32_pwr_reg_is_ready(struct regulator_dev *rdev)
+{
+ struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+ u32 val;
+
+ val = readl(priv->base + REG_PWR_CR3);
+
+ return (val & priv->ready_mask);
+}
+
+static int stm32_pwr_reg_is_enabled(struct regulator_dev *rdev)
+{
+ struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+ u32 val;
+
+ val = readl(priv->base + REG_PWR_CR3);
+
+ return (val & rdev->desc->enable_mask);
+}
+
+static int stm32_pwr_reg_enable(struct regulator_dev *rdev)
+{
+ struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+ struct stm32_pwr_desc *desc = to_desc(rdev);
+ int ret;
+ u32 val;
+
+ regulator_enable(priv->supply);
+
+ val = readl(priv->base + REG_PWR_CR3);
+ val |= rdev->desc->enable_mask;
+ writel(val, priv->base + REG_PWR_CR3);
+
+ /* use an arbitrary timeout of 20ms */
+ ret = readx_poll_timeout(stm32_pwr_reg_is_ready, rdev, val, val,
+ 20 * USEC_PER_MSEC);
+ if (ret)
+ dev_err(priv->dev, "%s: regulator enable timed out!\n",
+ desc->name);
+
+ return ret;
+}
+
+static int stm32_pwr_reg_disable(struct regulator_dev *rdev)
+{
+ struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+ struct stm32_pwr_desc *desc = to_desc(rdev);
+ int ret;
+ u32 val;
+
+ val = readl(priv->base + REG_PWR_CR3);
+ val &= ~rdev->desc->enable_mask;
+ writel(val, priv->base + REG_PWR_CR3);
+
+ /* use an arbitrary timeout of 20ms */
+ ret = readx_poll_timeout(stm32_pwr_reg_is_ready, rdev, val, !val,
+ 20 * USEC_PER_MSEC);
+ if (ret)
+ dev_err(priv->dev, "%s: regulator disable timed out!\n",
+ desc->name);
+
+ regulator_disable(priv->supply);
+
+ return ret;
+}
+
+static const struct regulator_ops stm32_pwr_reg_ops = {
+ .enable = stm32_pwr_reg_enable,
+ .disable = stm32_pwr_reg_disable,
+ .is_enabled = stm32_pwr_reg_is_enabled,
+};
+
+#define PWR_REG(_id, _name, _volt, _en, _supply) \
+ [_id] = { { \
+ .n_voltages = 1, \
+ .ops = &stm32_pwr_reg_ops, \
+ .min_uV = _volt, \
+ .enable_mask = _en, \
+ }, .name = _name, .supply_name = _supply, }
+
+static const struct stm32_pwr_desc stm32_pwr_desc[] = {
+ PWR_REG(PWR_REG11, "reg11", 1100000, REG_1_1_EN, "vdd"),
+ PWR_REG(PWR_REG18, "reg18", 1800000, REG_1_8_EN, "vdd"),
+ PWR_REG(PWR_USB33, "usb33", 3300000, USB_3_3_EN, "vdd_3v3_usbfs"),
+};
+
+static int stm32_pwr_regulator_probe(struct device_d *dev)
+{
+ struct stm32_pwr_reg *priv;
+ struct device_node *child;
+ struct resource *iores;
+ int i, ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ for_each_child_of_node(dev->device_node, child) {
+ const struct stm32_pwr_desc *desc = NULL;
+
+ for (i = 0; i < STM32PWR_REG_NUM_REGS; i++) {
+ const char *name;
+
+ name = of_get_property(child, "regulator-name", NULL);
+ if (name && strcmp(stm32_pwr_desc[i].name, name)) {
+ desc = &stm32_pwr_desc[i];
+ break;
+ }
+ }
+
+ if (!desc) {
+ dev_warn(dev, "Skipping unknown child node %s\n",
+ child->name);
+ continue;
+ }
+
+ priv = xzalloc(sizeof(*priv));
+ priv->base = IOMEM(iores->start);
+ priv->ready_mask = ready_mask_table[i];
+ priv->dev = dev;
+
+ priv->rdev.desc = &desc->desc;
+
+ priv->supply = regulator_get(dev, desc->supply_name);
+ if (IS_ERR(priv->supply))
+ return PTR_ERR(priv->supply);
+
+ ret = of_regulator_register(&priv->rdev, child);
+ if (ret) {
+ dev_err(dev, "%s: Failed to register regulator: %d\n",
+ desc->name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id stm32_pwr_of_match[] = {
+ { .compatible = "st,stm32mp1,pwr-reg", },
+ { /* sentinel */ },
+};
+
+static struct driver_d stm32_pwr_driver = {
+ .probe = stm32_pwr_regulator_probe,
+ .name = "stm32-pwr-regulator",
+ .of_compatible = stm32_pwr_of_match,
+};
+device_platform_driver(stm32_pwr_driver);
+
+MODULE_DESCRIPTION("STM32MP1 PWR voltage regulator driver");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");
--
2.25.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 4/6] phy: introduce phy_get_by_index
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum, mgr, rcz
In-Reply-To: <20200220100109.18147-1-a.fatoum@pengutronix.de>
The upstream (v5.6-rc1) device tree node of the stm32mp157c-dk2's OHCI
has a phys property, but not phy-names. We have no API to reference
such a phy easily (passing NULL isn't allowed). Add one.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/phy/phy-core.c | 14 ++++++++++++++
include/linux/phy/phy.h | 6 ++++++
2 files changed, 20 insertions(+)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index ad1e8147881b..ff6e35d16060 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -371,3 +371,17 @@ struct phy *phy_optional_get(struct device_d *dev, const char *string)
return phy;
}
+/**
+ * phy_get_by_index() - lookup and obtain a reference to a phy by index.
+ * @dev: device with node that references this phy
+ * @index: index of the phy
+ *
+ * Gets the phy using _of_phy_get()
+ */
+struct phy *phy_get_by_index(struct device_d *dev, int index)
+{
+ if (!dev->device_node)
+ return ERR_PTR(-ENODEV);
+
+ return _of_phy_get(dev->device_node, index);
+}
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index df480d4634ac..8a28b8e068b1 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -149,6 +149,7 @@ struct phy_provider *__of_phy_provider_register(struct device_d *dev,
struct of_phandle_args *args));
void of_phy_provider_unregister(struct phy_provider *phy_provider);
struct usb_phy *phy_to_usbphy(struct phy *phy);
+struct phy *phy_get_by_index(struct device_d *dev, int index);
#else
static inline int phy_init(struct phy *phy)
{
@@ -247,6 +248,11 @@ static inline struct usb_phy *phy_to_usbphy(struct phy *phy)
return NULL;
}
+static struct phy *phy_get_by_index(struct device_d *dev, int index)
+{
+ return ERR_PTR(-ENODEV);
+}
+
#endif
#endif /* __DRIVERS_PHY_H */
--
2.25.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 2/6] phy: remove unused init_data parameter
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum, mgr, rcz
In-Reply-To: <20200220100109.18147-1-a.fatoum@pengutronix.de>
Linux has since migrated to a new lookup API that lacks the init_data
parameter. As it's unused in barebox, follow suit.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 2 +-
drivers/phy/phy-core.c | 5 +----
drivers/phy/usb-nop-xceiv.c | 2 +-
drivers/pinctrl/pinctrl-tegra-xusb.c | 4 ++--
drivers/usb/imx/imx-usb-phy.c | 2 +-
include/linux/phy/phy.h | 8 ++------
6 files changed, 8 insertions(+), 15 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index 1aef2b300448..6d60eacd7ff9 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -106,7 +106,7 @@ static int imx8mq_usb_phy_probe(struct device_d *dev)
if (IS_ERR(imx_phy->base))
return PTR_ERR(imx_phy->base);
- imx_phy->phy = phy_create(dev, NULL, &imx8mq_usb_phy_ops, NULL);
+ imx_phy->phy = phy_create(dev, NULL, &imx8mq_usb_phy_ops);
if (IS_ERR(imx_phy->phy))
return PTR_ERR(imx_phy->phy);
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 066a887a2226..d1f3c7fde4a2 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -25,13 +25,11 @@ static int phy_ida;
* @dev: device that is creating the new phy
* @node: device node of the phy
* @ops: function pointers for performing phy operations
- * @init_data: contains the list of PHY consumers or NULL
*
* Called to create a phy using phy framework.
*/
struct phy *phy_create(struct device_d *dev, struct device_node *node,
- const struct phy_ops *ops,
- struct phy_init_data *init_data)
+ const struct phy_ops *ops)
{
int ret;
int id;
@@ -52,7 +50,6 @@ struct phy *phy_create(struct device_d *dev, struct device_node *node,
phy->dev.device_node = node ?: dev->device_node;
phy->id = id;
phy->ops = ops;
- phy->init_data = init_data;
ret = register_device(&phy->dev);
if (ret)
diff --git a/drivers/phy/usb-nop-xceiv.c b/drivers/phy/usb-nop-xceiv.c
index b124e6c0c48a..a9031fa7f8a8 100644
--- a/drivers/phy/usb-nop-xceiv.c
+++ b/drivers/phy/usb-nop-xceiv.c
@@ -107,7 +107,7 @@ static int nop_usbphy_probe(struct device_d *dev)
/* FIXME: Add vbus-detect-gpio support */
nopphy->usb_phy.dev = dev;
- nopphy->phy = phy_create(dev, NULL, &nop_phy_ops, NULL);
+ nopphy->phy = phy_create(dev, NULL, &nop_phy_ops);
if (IS_ERR(nopphy->phy)) {
ret = PTR_ERR(nopphy->phy);
goto release_gpio;
diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c
index e477280e62c3..c4d3bbe8d429 100644
--- a/drivers/pinctrl/pinctrl-tegra-xusb.c
+++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
@@ -415,7 +415,7 @@ static int pinctrl_tegra_xusb_probe(struct device_d *dev)
goto reset;
}
- phy = phy_create(dev, NULL, &pcie_phy_ops, NULL);
+ phy = phy_create(dev, NULL, &pcie_phy_ops);
if (IS_ERR(phy)) {
err = PTR_ERR(phy);
goto unregister;
@@ -424,7 +424,7 @@ static int pinctrl_tegra_xusb_probe(struct device_d *dev)
padctl->phys[TEGRA_XUSB_PADCTL_PCIE] = phy;
phy_set_drvdata(phy, padctl);
- phy = phy_create(dev, NULL, &sata_phy_ops, NULL);
+ phy = phy_create(dev, NULL, &sata_phy_ops);
if (IS_ERR(phy)) {
err = PTR_ERR(phy);
goto unregister;
diff --git a/drivers/usb/imx/imx-usb-phy.c b/drivers/usb/imx/imx-usb-phy.c
index d4562285c068..069dddcacbc7 100644
--- a/drivers/usb/imx/imx-usb-phy.c
+++ b/drivers/usb/imx/imx-usb-phy.c
@@ -174,7 +174,7 @@ static int imx_usbphy_probe(struct device_d *dev)
imxphy->usb_phy.dev = dev;
imxphy->usb_phy.notify_connect = imx_usbphy_notify_connect;
imxphy->usb_phy.notify_disconnect = imx_usbphy_notify_disconnect;
- imxphy->phy = phy_create(dev, NULL, &imx_phy_ops, NULL);
+ imxphy->phy = phy_create(dev, NULL, &imx_phy_ops);
if (IS_ERR(imxphy->phy)) {
ret = PTR_ERR(imxphy->phy);
goto err_clk;
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index 5d96e02df4fd..df480d4634ac 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -49,7 +49,6 @@ struct phy_attrs {
* @dev: phy device
* @id: id of the phy device
* @ops: function pointers for performing phy operations
- * @init_data: list of PHY consumers (non-dt only)
* @mutex: mutex to protect phy_ops
* @init_count: used to protect when the PHY is used by multiple consumers
* @power_count: used to protect when the PHY is used by multiple consumers
@@ -59,7 +58,6 @@ struct phy {
struct device_d dev;
int id;
const struct phy_ops *ops;
- struct phy_init_data *init_data;
int init_count;
int power_count;
struct phy_attrs attrs;
@@ -144,8 +142,7 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id);
struct phy *of_phy_simple_xlate(struct device_d *dev,
struct of_phandle_args *args);
struct phy *phy_create(struct device_d *dev, struct device_node *node,
- const struct phy_ops *ops,
- struct phy_init_data *init_data);
+ const struct phy_ops *ops);
void phy_destroy(struct phy *phy);
struct phy_provider *__of_phy_provider_register(struct device_d *dev,
struct phy * (*of_xlate)(struct device_d *dev,
@@ -225,8 +222,7 @@ static inline struct phy *of_phy_simple_xlate(struct device_d *dev,
static inline struct phy *phy_create(struct device_d *dev,
struct device_node *node,
- const struct phy_ops *ops,
- struct phy_init_data *init_data)
+ const struct phy_ops *ops)
{
return ERR_PTR(-ENOSYS);
}
--
2.25.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 6/6] phy: port over Linux stm32 usbphyc driver
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum, mgr, rcz
In-Reply-To: <20200220100109.18147-1-a.fatoum@pengutronix.de>
This ports over the Linux v5.6-rc1 state of the driver.
It'll be needed for both EHCI and DWC2 usb connectivity on the stm32mp1.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/phy/Kconfig | 13 +
drivers/phy/Makefile | 1 +
drivers/phy/phy-stm32-usbphyc.c | 434 ++++++++++++++++++++++++++++++++
3 files changed, 448 insertions(+)
create mode 100644 drivers/phy/phy-stm32-usbphyc.c
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index b5cefb2ff3d1..b5c8e98b9e48 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -24,4 +24,17 @@ config USB_NOP_XCEIV
source "drivers/phy/freescale/Kconfig"
+config PHY_STM32_USBPHYC
+ tristate "STM32 USB HS PHY Controller"
+ depends on ARCH_STM32MP
+ help
+ Enable this to support the High-Speed USB transceivers that are part
+ of some STMicroelectronics STM32 SoCs.
+
+ This driver controls the entire USB PHY block: the USB PHY controller
+ (USBPHYC) and the two 8-bit wide UTMI+ interfaces. First interface is
+ used by an HS USB Host controller, and the second one is shared
+ between an HS USB OTG controller and an HS USB Host controller,
+ selected by a USB switch.
+
endif
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 179c55e60abe..684aaed75aa2 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
obj-$(CONFIG_USB_NOP_XCEIV) += usb-nop-xceiv.o
obj-y += freescale/
+obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o
diff --git a/drivers/phy/phy-stm32-usbphyc.c b/drivers/phy/phy-stm32-usbphyc.c
new file mode 100644
index 000000000000..093842fe1460
--- /dev/null
+++ b/drivers/phy/phy-stm32-usbphyc.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STM32 USB PHY Controller driver
+ *
+ * Copyright (C) 2018 STMicroelectronics
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>.
+ */
+#include <common.h>
+#include <init.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <io.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+#include <asm-generic/div64.h>
+#include <usb/phy.h>
+
+#define STM32_USBPHYC_PLL 0x0
+#define STM32_USBPHYC_MISC 0x8
+#define STM32_USBPHYC_VERSION 0x3F4
+
+/* STM32_USBPHYC_PLL bit fields */
+#define PLLNDIV GENMASK(6, 0)
+#define PLLFRACIN GENMASK(25, 10)
+#define PLLEN BIT(26)
+#define PLLSTRB BIT(27)
+#define PLLSTRBYP BIT(28)
+#define PLLFRACCTL BIT(29)
+#define PLLDITHEN0 BIT(30)
+#define PLLDITHEN1 BIT(31)
+
+/* STM32_USBPHYC_MISC bit fields */
+#define SWITHOST BIT(0)
+
+/* STM32_USBPHYC_VERSION bit fields */
+#define MINREV GENMASK(3, 0)
+#define MAJREV GENMASK(7, 4)
+
+static const char * const supplies_names[] = {
+ "vdda1v1", /* 1V1 */
+ "vdda1v8", /* 1V8 */
+};
+
+#define NUM_SUPPLIES ARRAY_SIZE(supplies_names)
+
+#define PLL_LOCK_TIME_US 100
+#define PLL_PWR_DOWN_TIME_US 5
+#define PLL_FVCO_MHZ 2880
+#define PLL_INFF_MIN_RATE_HZ 19200000
+#define PLL_INFF_MAX_RATE_HZ 38400000
+#define HZ_PER_MHZ 1000000L
+
+struct pll_params {
+ u8 ndiv;
+ u16 frac;
+};
+
+struct stm32_usbphyc_phy {
+ struct phy *phy;
+ struct stm32_usbphyc *usbphyc;
+ struct regulator_bulk_data supplies[NUM_SUPPLIES];
+ u32 index;
+ bool active;
+};
+
+struct stm32_usbphyc {
+ struct device_d *dev;
+ void __iomem *base;
+ struct clk *clk;
+ struct stm32_usbphyc_phy **phys;
+ int nphys;
+ int switch_setup;
+};
+
+static inline void stm32_usbphyc_set_bits(void __iomem *reg, u32 bits)
+{
+ writel(readl(reg) | bits, reg);
+}
+
+static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits)
+{
+ writel(readl(reg) & ~bits, reg);
+}
+
+static void stm32_usbphyc_get_pll_params(u32 clk_rate,
+ struct pll_params *pll_params)
+{
+ unsigned long long fvco, ndiv, frac;
+
+ /* _
+ * | FVCO = INFF*2*(NDIV + FRACT/2^16) when DITHER_DISABLE[1] = 1
+ * | FVCO = 2880MHz
+ * <
+ * | NDIV = integer part of input bits to set the LDF
+ * |_FRACT = fractional part of input bits to set the LDF
+ * => PLLNDIV = integer part of (FVCO / (INFF*2))
+ * => PLLFRACIN = fractional part of(FVCO / INFF*2) * 2^16
+ * <=> PLLFRACIN = ((FVCO / (INFF*2)) - PLLNDIV) * 2^16
+ */
+ fvco = (unsigned long long)PLL_FVCO_MHZ * HZ_PER_MHZ;
+
+ ndiv = fvco;
+ do_div(ndiv, (clk_rate * 2));
+ pll_params->ndiv = (u8)ndiv;
+
+ frac = fvco * (1 << 16);
+ do_div(frac, (clk_rate * 2));
+ frac = frac - (ndiv * (1 << 16));
+ pll_params->frac = (u16)frac;
+}
+
+static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc)
+{
+ struct pll_params pll_params;
+ u32 clk_rate = clk_get_rate(usbphyc->clk);
+ u32 ndiv, frac;
+ u32 usbphyc_pll;
+
+ if ((clk_rate < PLL_INFF_MIN_RATE_HZ) ||
+ (clk_rate > PLL_INFF_MAX_RATE_HZ)) {
+ dev_err(usbphyc->dev, "input clk freq (%dHz) out of range\n",
+ clk_rate);
+ return -EINVAL;
+ }
+
+ stm32_usbphyc_get_pll_params(clk_rate, &pll_params);
+ ndiv = FIELD_PREP(PLLNDIV, pll_params.ndiv);
+ frac = FIELD_PREP(PLLFRACIN, pll_params.frac);
+
+ usbphyc_pll = PLLDITHEN1 | PLLDITHEN0 | PLLSTRBYP | ndiv;
+
+ if (pll_params.frac)
+ usbphyc_pll |= PLLFRACCTL | frac;
+
+ writel(usbphyc_pll, usbphyc->base + STM32_USBPHYC_PLL);
+
+ dev_dbg(usbphyc->dev, "input clk freq=%dHz, ndiv=%lu, frac=%lu\n",
+ clk_rate, FIELD_GET(PLLNDIV, usbphyc_pll),
+ FIELD_GET(PLLFRACIN, usbphyc_pll));
+
+ return 0;
+}
+
+static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc)
+{
+ int i;
+
+ for (i = 0; i < usbphyc->nphys; i++)
+ if (usbphyc->phys[i]->active)
+ return true;
+
+ return false;
+}
+
+static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc)
+{
+ void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+ bool pllen = (readl(pll_reg) & PLLEN);
+ int ret;
+
+ /* Check if one phy port has already configured the pll */
+ if (pllen && stm32_usbphyc_has_one_phy_active(usbphyc))
+ return 0;
+
+ if (pllen) {
+ stm32_usbphyc_clr_bits(pll_reg, PLLEN);
+ /* Wait for minimum width of powerdown pulse (ENABLE = Low) */
+ udelay(PLL_PWR_DOWN_TIME_US);
+ }
+
+ ret = stm32_usbphyc_pll_init(usbphyc);
+ if (ret)
+ return ret;
+
+ stm32_usbphyc_set_bits(pll_reg, PLLEN);
+
+ /* Wait for maximum lock time */
+ udelay(PLL_LOCK_TIME_US);
+
+ if (!(readl(pll_reg) & PLLEN)) {
+ dev_err(usbphyc->dev, "PLLEN not set\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
+{
+ void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+
+ /* Check if other phy port active */
+ if (stm32_usbphyc_has_one_phy_active(usbphyc))
+ return 0;
+
+ stm32_usbphyc_clr_bits(pll_reg, PLLEN);
+ /* Wait for minimum width of powerdown pulse (ENABLE = Low) */
+ udelay(PLL_PWR_DOWN_TIME_US);
+
+ if (readl(pll_reg) & PLLEN) {
+ dev_err(usbphyc->dev, "PLL not reset\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int stm32_usbphyc_phy_init(struct phy *phy)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+ struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
+ int ret;
+
+ ret = stm32_usbphyc_pll_enable(usbphyc);
+ if (ret)
+ return ret;
+
+ usbphyc_phy->active = true;
+
+ return 0;
+}
+
+static int stm32_usbphyc_phy_exit(struct phy *phy)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+ struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
+
+ usbphyc_phy->active = false;
+
+ return stm32_usbphyc_pll_disable(usbphyc);
+}
+
+static int stm32_usbphyc_phy_power_on(struct phy *phy)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+
+ return regulator_bulk_enable(NUM_SUPPLIES, usbphyc_phy->supplies);
+}
+
+static int stm32_usbphyc_phy_power_off(struct phy *phy)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+
+ return regulator_bulk_disable(NUM_SUPPLIES, usbphyc_phy->supplies);
+}
+
+static const struct phy_ops stm32_usbphyc_phy_ops = {
+ .init = stm32_usbphyc_phy_init,
+ .exit = stm32_usbphyc_phy_exit,
+ .power_on = stm32_usbphyc_phy_power_on,
+ .power_off = stm32_usbphyc_phy_power_off,
+};
+
+static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
+ u32 utmi_switch)
+{
+ if (!utmi_switch)
+ stm32_usbphyc_clr_bits(usbphyc->base + STM32_USBPHYC_MISC,
+ SWITHOST);
+ else
+ stm32_usbphyc_set_bits(usbphyc->base + STM32_USBPHYC_MISC,
+ SWITHOST);
+ usbphyc->switch_setup = utmi_switch;
+}
+
+static struct phy *stm32_usbphyc_of_xlate(struct device_d *dev,
+ struct of_phandle_args *args)
+{
+ struct stm32_usbphyc *usbphyc = dev->priv;
+ struct stm32_usbphyc_phy *usbphyc_phy = NULL;
+ struct device_node *phynode = args->np;
+ int port = 0;
+
+ for (port = 0; port < usbphyc->nphys; port++) {
+ if (phynode == usbphyc->phys[port]->phy->dev.device_node) {
+ usbphyc_phy = usbphyc->phys[port];
+ break;
+ }
+ }
+
+ if (!usbphyc_phy) {
+ dev_err(dev, "failed to find phy\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (((usbphyc_phy->index == 0) && (args->args_count != 0)) ||
+ ((usbphyc_phy->index == 1) && (args->args_count != 1))) {
+ dev_err(dev, "invalid number of cells for phy port%d\n",
+ usbphyc_phy->index);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Configure the UTMI switch for PHY port#2 */
+ if (usbphyc_phy->index == 1) {
+ if (usbphyc->switch_setup < 0) {
+ stm32_usbphyc_switch_setup(usbphyc, args->args[0]);
+ } else {
+ if (args->args[0] != usbphyc->switch_setup) {
+ dev_err(dev, "phy port1 already used\n");
+ return ERR_PTR(-EBUSY);
+ }
+ }
+ }
+
+ return usbphyc_phy->phy;
+}
+
+static int stm32_usbphyc_probe(struct device_d *dev)
+{
+ struct stm32_usbphyc *usbphyc;
+ struct device_node *child, *np = dev->device_node;
+ struct resource *iores;
+ struct phy_provider *phy_provider;
+ u32 version;
+ int ret, port = 0;
+
+ usbphyc = xzalloc(sizeof(*usbphyc));
+
+ usbphyc->dev = dev;
+ dev->priv = usbphyc;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ usbphyc->base = IOMEM(iores->start);
+
+ usbphyc->clk = clk_get(dev, 0);
+ if (IS_ERR(usbphyc->clk)) {
+ ret = PTR_ERR(usbphyc->clk);
+ dev_err(dev, "clk get failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(usbphyc->clk);
+ if (ret) {
+ dev_err(dev, "clk enable failed: %d\n", ret);
+ return ret;
+ }
+
+ device_reset_us(dev, 2);
+
+ usbphyc->switch_setup = -EINVAL;
+ usbphyc->nphys = of_get_child_count(np);
+ usbphyc->phys = xzalloc(usbphyc->nphys * sizeof(*usbphyc->phys));
+
+ for_each_child_of_node(np, child) {
+ struct stm32_usbphyc_phy *usbphyc_phy;
+ struct phy *phy;
+ u32 index;
+ int i;
+
+ phy = phy_create(dev, child, &stm32_usbphyc_phy_ops);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to create phy%d: %d\n",
+ port, ret);
+ goto clk_disable;
+ }
+
+ usbphyc_phy = xzalloc(sizeof(*usbphyc_phy));
+
+ for (i = 0; i < NUM_SUPPLIES; i++)
+ usbphyc_phy->supplies[i].supply = supplies_names[i];
+
+ ret = regulator_bulk_get(&phy->dev, NUM_SUPPLIES,
+ usbphyc_phy->supplies);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&phy->dev,
+ "failed to get regulators: %d\n", ret);
+ goto clk_disable;
+ }
+
+ ret = of_property_read_u32(child, "reg", &index);
+ if (ret || index > usbphyc->nphys) {
+ dev_err(&phy->dev, "invalid reg property: %d\n", ret);
+ goto clk_disable;
+ }
+
+ usbphyc->phys[port] = usbphyc_phy;
+ phy_set_bus_width(phy, 8);
+ phy_set_drvdata(phy, usbphyc_phy);
+
+ usbphyc->phys[port]->phy = phy;
+ usbphyc->phys[port]->usbphyc = usbphyc;
+ usbphyc->phys[port]->index = index;
+ usbphyc->phys[port]->active = false;
+
+ port++;
+ }
+
+ phy_provider = of_phy_provider_register(dev, stm32_usbphyc_of_xlate);
+ if (IS_ERR(phy_provider)) {
+ ret = PTR_ERR(phy_provider);
+ dev_err(dev, "failed to register phy provider: %d\n", ret);
+ goto clk_disable;
+ }
+
+ version = readl(usbphyc->base + STM32_USBPHYC_VERSION);
+ dev_info(dev, "registered rev: %lu.%lu\n",
+ FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version));
+
+ return 0;
+
+clk_disable:
+ clk_disable(usbphyc->clk);
+
+ return ret;
+}
+
+static void stm32_usbphyc_remove(struct device_d *dev)
+{
+ struct stm32_usbphyc *usbphyc = dev->priv;
+
+ clk_disable(usbphyc->clk);
+}
+
+static const struct of_device_id stm32_usbphyc_of_match[] = {
+ { .compatible = "st,stm32mp1-usbphyc", },
+ { /* sentinel */ },
+};
+
+static struct driver_d stm32_usbphyc_driver = {
+ .name = "stm32-usbphyc",
+ .probe = stm32_usbphyc_probe,
+ .remove = stm32_usbphyc_remove,
+ .of_compatible = stm32_usbphyc_of_match,
+};
+device_platform_driver(stm32_usbphyc_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics STM32 USBPHYC driver");
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_LICENSE("GPL v2");
--
2.25.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 3/6] phy: populate existing ->pwr member with phy-supply
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum, mgr, rcz
In-Reply-To: <20200220100109.18147-1-a.fatoum@pengutronix.de>
barebox already enables/disables the ->pwr regulator at the correct
places, but doesn't assign a value anywhere. Initialize it with the
phy-supply regulator like Linux does.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/phy/phy-core.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index d1f3c7fde4a2..ad1e8147881b 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -51,6 +51,16 @@ struct phy *phy_create(struct device_d *dev, struct device_node *node,
phy->id = id;
phy->ops = ops;
+ /* phy-supply */
+ phy->pwr = regulator_get(&phy->dev, "phy");
+ if (IS_ERR(phy->pwr)) {
+ ret = PTR_ERR(phy->pwr);
+ if (ret == -EPROBE_DEFER)
+ goto free_ida;
+
+ phy->pwr = NULL;
+ }
+
ret = register_device(&phy->dev);
if (ret)
goto free_ida;
--
2.25.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* Re: [Xen-devel] [PATCH 2/3] tools/xentop: Remove dead code
From: Wei Liu @ 2020-02-20 0:51 UTC (permalink / raw)
To: Sander Eikelenboom; +Cc: xen-devel, Ian Jackson, Wei Liu
In-Reply-To: <a5d34e806a0798e411b405625d1ef74db998fc85.1582143896.git.linux@eikelenboom.it>
On Wed, Feb 19, 2020 at 09:31:31PM +0100, Sander Eikelenboom wrote:
> Signed-off-by: Sander Eikelenboom <linux@eikelenboom.it>
> ---
I will add the following commit message:
The variable freealbe_mb was never really used. Delete it and the code
associated with it.
Acked-by: Wei Liu <wl@xen.org>
> tools/xenstat/xentop/xentop.c | 10 ++--------
> 1 file changed, 2 insertions(+), 8 deletions(-)
>
> diff --git a/tools/xenstat/xentop/xentop.c b/tools/xenstat/xentop/xentop.c
> index 13b612f26d..bbb5d47b76 100644
> --- a/tools/xenstat/xentop/xentop.c
> +++ b/tools/xenstat/xentop/xentop.c
> @@ -943,7 +943,6 @@ void do_summary(void)
> crash = 0, dying = 0, shutdown = 0;
> unsigned i, num_domains = 0;
> unsigned long long used = 0;
> - long freeable_mb = 0;
> xenstat_domain *domain;
> time_t curt;
>
> @@ -970,17 +969,12 @@ void do_summary(void)
> num_domains, run, block, pause, crash, dying, shutdown);
>
> used = xenstat_node_tot_mem(cur_node)-xenstat_node_free_mem(cur_node);
> - freeable_mb = 0;
>
> /* Dump node memory and cpu information */
> - if ( freeable_mb <= 0 )
> - print("Mem: %lluk total, %lluk used, %lluk free ",
> + print("Mem: %lluk total, %lluk used, %lluk free ",
> xenstat_node_tot_mem(cur_node)/1024, used/1024,
> xenstat_node_free_mem(cur_node)/1024);
> - else
> - print("Mem: %lluk total, %lluk used, %lluk free, %ldk freeable, ",
> - xenstat_node_tot_mem(cur_node)/1024, used/1024,
> - xenstat_node_free_mem(cur_node)/1024, freeable_mb*1024);
> +
> print("CPUs: %u @ %lluMHz\n",
> xenstat_node_num_cpus(cur_node),
> xenstat_node_cpu_hz(cur_node)/1000000);
> --
> 2.20.1
>
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel
^ permalink raw reply
* Re: [Xen-devel] [PATCH 3/3] tools/xentop: Cleanup some trailing whitespace
From: Wei Liu @ 2020-02-20 0:51 UTC (permalink / raw)
To: Sander Eikelenboom; +Cc: xen-devel, Ian Jackson, Wei Liu
In-Reply-To: <4fd751d1a45632f9ed0a32b7fc7988343ad03f83.1582143896.git.linux@eikelenboom.it>
On Wed, Feb 19, 2020 at 09:31:32PM +0100, Sander Eikelenboom wrote:
> Signed-off-by: Sander Eikelenboom <linux@eikelenboom.it>
Acked-by: Wei Liu <wl@xen.org>
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel
^ permalink raw reply
* Re: [PATCH qemu v7 0/5] spapr: Kill SLOF
From: Paolo Bonzini @ 2020-02-20 10:01 UTC (permalink / raw)
To: Alexey Kardashevskiy, qemu-devel; +Cc: qemu-ppc, David Gibson
In-Reply-To: <20200220061622.15064-1-aik@ozlabs.ru>
On 20/02/20 07:16, Alexey Kardashevskiy wrote:
> This is another attempt to implement minimalistic
> Open Firmware Client Interface in QEMU.
>
> With this thing, I can boot unmodified Ubuntu 18.04 and Fedora 30
> directly from the disk without SLOF.
>
> A useful discussion happened esrlier:
> https://lore.kernel.org/qemu-devel/f881c2e7-be92-9695-6e19-2dd88cbc63c1@ozlabs.ru/
>
> 5/5 is kind of controvertial though. This respin does not include
> networking.
>
> This is based on sha1
> 015fb0ead60d Chen Qun "hw/ppc/virtex_ml507:fix leak of fdevice tree blob".
I would like to play with this. Can you provide a disk image that just
reads the first sector of the disk using an OpenFirmware read command,
and dumps it to stdout? (Also, I lost the pointer to your super-minimal
pSeries firmware).
Thanks,
Paolo
^ permalink raw reply
* Re: [dpdk-dev] [PATCH] app/testpmd: fix identifier size for port attach
From: Iremonger, Bernard @ 2020-02-20 10:03 UTC (permalink / raw)
To: Wisam Jaddo, Lu, Wenzhuo, dev@dpdk.org, rasland@mellanox.com,
matan@mellanox.com
Cc: mukawa@igel.co.jp, stable@dpdk.org
In-Reply-To: <1582130850-6958-1-git-send-email-wisamm@mellanox.com>
> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of Wisam Jaddo
> Sent: Wednesday, February 19, 2020 4:48 PM
> To: Lu, Wenzhuo <wenzhuo.lu@intel.com>; dev@dpdk.org;
> rasland@mellanox.com; matan@mellanox.com
> Cc: mukawa@igel.co.jp; stable@dpdk.org
> Subject: [dpdk-dev] [PATCH] app/testpmd: fix identifier size for port attach
>
> Identifier for new port may contain white list options, and white list options
> will not fit into 128 from STR_TOKEN_SIZE, instead having 4096 char from
> STR_MULTI_TOKEN_SIZE will provide better and more options
>
> Fixes: edab33b1c01d ("app/testpmd: support port hotplug")
> Cc: mukawa@igel.co.jp
> Cc: stable@dpdk.org
>
> Signed-off-by: Wisam Jaddo <wisamm@mellanox.com>
Acked-by: Bernard Iremonger <bernard.iremonger@intel.com>
^ permalink raw reply
* [PATCH 1/8] powerpc: Add asm header 'papr_scm.h' describing the papr-scm interface
From: Vaibhav Jain @ 2020-02-20 9:57 UTC (permalink / raw)
To: linuxppc-dev
Cc: Vaibhav Jain, Michael Ellerman, Oliver O'Halloran,
Alastair D'Silva, Aneesh Kumar K . V
In-Reply-To: <20200220095805.197229-1-vaibhav@linux.ibm.com>
Add a new powerpc specific asm header named 'papr-scm.h' that descibes
the interface between PHYP and guest kernel running as an LPAR.
The HCALLs specific to managing SCM are descibed in Ref[1]. The asm
header introduced by this patch however describes the data structures
exchanged between PHYP and kernel during those HCALLs.
Future patches will use these structures to provide support for
retriving nvdimm health and performance stats in papr_scm kernel
module.
[1]: commit 58b278f568f0 ("powerpc: Provide initial documentation for
PAPR hcalls")
Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>
---
arch/powerpc/include/asm/papr_scm.h | 68 +++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
create mode 100644 arch/powerpc/include/asm/papr_scm.h
diff --git a/arch/powerpc/include/asm/papr_scm.h b/arch/powerpc/include/asm/papr_scm.h
new file mode 100644
index 000000000000..d893621063f3
--- /dev/null
+++ b/arch/powerpc/include/asm/papr_scm.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Structures and defines needed to manage nvdimms for spapr guests.
+ */
+#ifndef _ASM_POWERPC_PAPR_SCM_H_
+#define _ASM_POWERPC_PAPR_SCM_H_
+
+#include <linux/types.h>
+#include <asm/bitsperlong.h>
+#include <linux/stringify.h>
+
+/* DIMM health bitmap bitmap indicators */
+/* SCM device is unable to persist memory contents */
+#define PAPR_SCM_DIMM_UNARMED PPC_BIT(0)
+/* SCM device failed to persist memory contents */
+#define PAPR_SCM_DIMM_SHUTDOWN_DIRTY PPC_BIT(1)
+/* SCM device contents are persisted from previous IPL */
+#define PAPR_SCM_DIMM_SHUTDOWN_CLEAN PPC_BIT(2)
+/* SCM device contents are not persisted from previous IPL */
+#define PAPR_SCM_DIMM_EMPTY PPC_BIT(3)
+/* SCM device memory life remaining is critically low */
+#define PAPR_SCM_DIMM_HEALTH_CRITICAL PPC_BIT(4)
+/* SCM device will be garded off next IPL due to failure */
+#define PAPR_SCM_DIMM_HEALTH_FATAL PPC_BIT(5)
+/* SCM contents cannot persist due to current platform health status */
+#define PAPR_SCM_DIMM_HEALTH_UNHEALTHY PPC_BIT(6)
+/* SCM device is unable to persist memory contents in certain conditions */
+#define PAPR_SCM_DIMM_HEALTH_NON_CRITICAL PPC_BIT(7)
+/* SCM device is encrypted */
+#define PAPR_SCM_DIMM_ENCRYPTED PPC_BIT(8)
+/* SCM device has been scrubbed and locked */
+#define PAPR_SCM_DIMM_SCRUBBED_AND_LOCKED PPC_BIT(9)
+
+/* Bits status indicators for health bitmap indicating unarmed dimm */
+#define PAPR_SCM_DIMM_UNARMED_MASK (PAPR_SCM_DIMM_UNARMED | \
+ PAPR_SCM_DIMM_HEALTH_UNHEALTHY | \
+ PAPR_SCM_DIMM_HEALTH_NON_CRITICAL)
+
+/* Bits status indicators for health bitmap indicating unflushed dimm */
+#define PAPR_SCM_DIMM_BAD_SHUTDOWN_MASK (PAPR_SCM_DIMM_SHUTDOWN_DIRTY)
+
+/* Bits status indicators for health bitmap indicating unrestored dimm */
+#define PAPR_SCM_DIMM_BAD_RESTORE_MASK (PAPR_SCM_DIMM_EMPTY)
+
+/* Bit status indicators for smart event notification */
+#define PAPR_SCM_DIMM_SMART_EVENT_MASK (PAPR_SCM_DIMM_HEALTH_CRITICAL | \
+ PAPR_SCM_DIMM_HEALTH_FATAL | \
+ PAPR_SCM_DIMM_HEALTH_UNHEALTHY | \
+ PAPR_SCM_DIMM_HEALTH_NON_CRITICAL)
+
+#define PAPR_SCM_PERF_STATS_EYECATCHER __stringify(SCMSTATS)
+
+/* Struct holding a single performance metric */
+struct papr_scm_perf_stat {
+ __be64 statistic_id;
+ __be64 statistic_value;
+};
+
+/* Struct exchanged between kernel and ndctl reporting drc perf stats */
+struct papr_scm_perf_stats {
+ uint8_t eye_catcher[8];
+ __be32 stats_version; /* Should be 0x01 */
+ __be32 num_statistics; /* Number of stats following */
+ /* zero or more performance matrics */
+ struct papr_scm_perf_stat scm_statistics[];
+} __packed;
+
+#endif
--
2.24.1
^ permalink raw reply related
* Re: [Xen-devel] [PATCH 0/3] tools/xentop: Fix calculation of used memory and some cleanups
From: Wei Liu @ 2020-02-20 0:53 UTC (permalink / raw)
To: Sander Eikelenboom; +Cc: xen-devel, Ian Jackson, Wei Liu
In-Reply-To: <cover.1582143896.git.linux@eikelenboom.it>
On Wed, Feb 19, 2020 at 09:31:29PM +0100, Sander Eikelenboom wrote:
> Fixes some fallout from: c588c002cc1 ('tools: remove tmem code and commands')
Thanks. I made some suggestions on adding commit messages. Let me know
if you're okay with those.
Wei.
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel
^ permalink raw reply
* [RESEND RFC PATCH v3] clk: Use new helper in managed functions
From: Marc Gonzalez @ 2020-02-20 10:04 UTC (permalink / raw)
To: Stephen Boyd, Michael Turquette, Kuninori Morimoto, Russell King,
Sudip Mukherjee, Dmitry Torokhov, Guenter Roeck, Bjorn Andersson,
Robin Murphy, Geert Uytterhoeven, Arnd Bergmann, Ard Biesheuvel,
Greg Kroah-Hartman, Rafael Wysocki, Suzuki Poulose, Mark Rutland
Cc: linux-clk, Linux ARM, LKML
Introduce devm_add() to wrap devres_alloc() / devres_add() calls.
Using that helper produces simpler code, and smaller object size.
E.g. with gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu:
text data bss dec hex filename
- 1708 80 0 1788 6fc drivers/clk/clk-devres.o
+ 1524 80 0 1604 644 drivers/clk/clk-devres.o
Signed-off-by: Marc Gonzalez <marc.w.gonzalez@free.fr>
---
Differences from v2 to v3
x Make devm_add() return an error-code rather than the raw data pointer
(in case devres_alloc ever returns an ERR_PTR) as suggested by Geert
x Provide a variadic version devm_vadd() to work with structs as suggested
by Geert
x Don't use nested ifs in clk_devm* implementations (hopefully simpler
code logic to follow) as suggested by Geert
Questions:
x This patch might need to be split in two? (Introduce the new API, then use it)
x Convert other subsystems to show the value of this proposal?
x Maybe comment the API usage somewhere
---
drivers/base/devres.c | 15 ++++++
drivers/clk/clk-devres.c | 99 ++++++++++++++--------------------------
include/linux/device.h | 3 ++
3 files changed, 53 insertions(+), 64 deletions(-)
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 0bbb328bd17f..b2603789755b 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -685,6 +685,21 @@ int devres_release_group(struct device *dev, void *id)
}
EXPORT_SYMBOL_GPL(devres_release_group);
+int devm_add(struct device *dev, dr_release_t func, void *arg, size_t size)
+{
+ void *data = devres_alloc(func, size, GFP_KERNEL);
+
+ if (!data) {
+ func(dev, arg);
+ return -ENOMEM;
+ }
+
+ memcpy(data, arg, size);
+ devres_add(dev, data);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_add);
+
/*
* Custom devres actions allow inserting a simple function call
* into the teadown sequence.
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
index be160764911b..1da69d273855 100644
--- a/drivers/clk/clk-devres.c
+++ b/drivers/clk/clk-devres.c
@@ -4,26 +4,22 @@
#include <linux/export.h>
#include <linux/gfp.h>
-static void devm_clk_release(struct device *dev, void *res)
+static void my_clk_put(struct device *dev, void *res)
{
clk_put(*(struct clk **)res);
}
struct clk *devm_clk_get(struct device *dev, const char *id)
{
- struct clk **ptr, *clk;
-
- ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- clk = clk_get(dev, id);
- if (!IS_ERR(clk)) {
- *ptr = clk;
- devres_add(dev, ptr);
- } else {
- devres_free(ptr);
- }
+ int ret;
+ struct clk *clk = clk_get(dev, id);
+
+ if (IS_ERR(clk))
+ return clk;
+
+ ret = devm_add(dev, my_clk_put, &clk, sizeof(clk));
+ if (ret)
+ return ERR_PTR(ret);
return clk;
}
@@ -40,14 +36,14 @@ struct clk *devm_clk_get_optional(struct device *dev, const char *id)
}
EXPORT_SYMBOL(devm_clk_get_optional);
-struct clk_bulk_devres {
- struct clk_bulk_data *clks;
+struct clk_bulk_args {
int num_clks;
+ struct clk_bulk_data *clks;
};
-static void devm_clk_bulk_release(struct device *dev, void *res)
+static void my_clk_bulk_put(struct device *dev, void *res)
{
- struct clk_bulk_devres *devres = res;
+ struct clk_bulk_args *devres = res;
clk_bulk_put(devres->num_clks, devres->clks);
}
@@ -55,25 +51,17 @@ static void devm_clk_bulk_release(struct device *dev, void *res)
static int __devm_clk_bulk_get(struct device *dev, int num_clks,
struct clk_bulk_data *clks, bool optional)
{
- struct clk_bulk_devres *devres;
int ret;
- devres = devres_alloc(devm_clk_bulk_release,
- sizeof(*devres), GFP_KERNEL);
- if (!devres)
- return -ENOMEM;
-
if (optional)
ret = clk_bulk_get_optional(dev, num_clks, clks);
else
ret = clk_bulk_get(dev, num_clks, clks);
- if (!ret) {
- devres->clks = clks;
- devres->num_clks = num_clks;
- devres_add(dev, devres);
- } else {
- devres_free(devres);
- }
+
+ if (ret)
+ return ret;
+
+ ret = devm_vadd(dev, my_clk_bulk_put, clk_bulk_args, num_clks, clks);
return ret;
}
@@ -95,24 +83,15 @@ EXPORT_SYMBOL_GPL(devm_clk_bulk_get_optional);
int __must_check devm_clk_bulk_get_all(struct device *dev,
struct clk_bulk_data **clks)
{
- struct clk_bulk_devres *devres;
int ret;
+ int num_clks = clk_bulk_get_all(dev, clks);
- devres = devres_alloc(devm_clk_bulk_release,
- sizeof(*devres), GFP_KERNEL);
- if (!devres)
- return -ENOMEM;
-
- ret = clk_bulk_get_all(dev, &devres->clks);
- if (ret > 0) {
- *clks = devres->clks;
- devres->num_clks = ret;
- devres_add(dev, devres);
- } else {
- devres_free(devres);
- }
+ if (num_clks <= 0)
+ return num_clks;
- return ret;
+ ret = devm_vadd(dev, my_clk_bulk_put, clk_bulk_args, num_clks, *clks);
+
+ return ret ? : num_clks;
}
EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all);
@@ -128,30 +107,22 @@ static int devm_clk_match(struct device *dev, void *res, void *data)
void devm_clk_put(struct device *dev, struct clk *clk)
{
- int ret;
-
- ret = devres_release(dev, devm_clk_release, devm_clk_match, clk);
-
- WARN_ON(ret);
+ WARN_ON(devres_release(dev, my_clk_put, devm_clk_match, clk));
}
EXPORT_SYMBOL(devm_clk_put);
struct clk *devm_get_clk_from_child(struct device *dev,
struct device_node *np, const char *con_id)
{
- struct clk **ptr, *clk;
-
- ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- clk = of_clk_get_by_name(np, con_id);
- if (!IS_ERR(clk)) {
- *ptr = clk;
- devres_add(dev, ptr);
- } else {
- devres_free(ptr);
- }
+ int ret;
+ struct clk *clk = of_clk_get_by_name(np, con_id);
+
+ if (IS_ERR(clk))
+ return clk;
+
+ ret = devm_add(dev, my_clk_put, &clk, sizeof(clk));
+ if (ret)
+ return ERR_PTR(ret);
return clk;
}
diff --git a/include/linux/device.h b/include/linux/device.h
index e226030c1df3..9f18582fc0f9 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -969,6 +969,9 @@ void __iomem *devm_of_iomap(struct device *dev,
struct device_node *node, int index,
resource_size_t *size);
+int devm_add(struct device *dev, dr_release_t func, void *arg, size_t size);
+#define devm_vadd(dev, func, type, args...) \
+ devm_add(dev, func, &(struct type){args}, sizeof(struct type))
/* allows to add/remove a custom action to devres stack */
int devm_add_action(struct device *dev, void (*action)(void *), void *data);
void devm_remove_action(struct device *dev, void (*action)(void *), void *data);
--
2.17.1
^ permalink raw reply related
* [RESEND RFC PATCH v3] clk: Use new helper in managed functions
From: Marc Gonzalez @ 2020-02-20 10:04 UTC (permalink / raw)
To: Stephen Boyd, Michael Turquette, Kuninori Morimoto, Russell King,
Sudip Mukherjee, Dmitry Torokhov, Guenter Roeck, Bjorn Andersson,
Robin Murphy, Geert Uytterhoeven, Arnd Bergmann, Ard Biesheuvel,
Greg Kroah-Hartman, Rafael Wysocki, Suzuki Poulose, Mark Rutland
Cc: linux-clk, Linux ARM, LKML
Introduce devm_add() to wrap devres_alloc() / devres_add() calls.
Using that helper produces simpler code, and smaller object size.
E.g. with gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu:
text data bss dec hex filename
- 1708 80 0 1788 6fc drivers/clk/clk-devres.o
+ 1524 80 0 1604 644 drivers/clk/clk-devres.o
Signed-off-by: Marc Gonzalez <marc.w.gonzalez@free.fr>
---
Differences from v2 to v3
x Make devm_add() return an error-code rather than the raw data pointer
(in case devres_alloc ever returns an ERR_PTR) as suggested by Geert
x Provide a variadic version devm_vadd() to work with structs as suggested
by Geert
x Don't use nested ifs in clk_devm* implementations (hopefully simpler
code logic to follow) as suggested by Geert
Questions:
x This patch might need to be split in two? (Introduce the new API, then use it)
x Convert other subsystems to show the value of this proposal?
x Maybe comment the API usage somewhere
---
drivers/base/devres.c | 15 ++++++
drivers/clk/clk-devres.c | 99 ++++++++++++++--------------------------
include/linux/device.h | 3 ++
3 files changed, 53 insertions(+), 64 deletions(-)
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 0bbb328bd17f..b2603789755b 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -685,6 +685,21 @@ int devres_release_group(struct device *dev, void *id)
}
EXPORT_SYMBOL_GPL(devres_release_group);
+int devm_add(struct device *dev, dr_release_t func, void *arg, size_t size)
+{
+ void *data = devres_alloc(func, size, GFP_KERNEL);
+
+ if (!data) {
+ func(dev, arg);
+ return -ENOMEM;
+ }
+
+ memcpy(data, arg, size);
+ devres_add(dev, data);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_add);
+
/*
* Custom devres actions allow inserting a simple function call
* into the teadown sequence.
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
index be160764911b..1da69d273855 100644
--- a/drivers/clk/clk-devres.c
+++ b/drivers/clk/clk-devres.c
@@ -4,26 +4,22 @@
#include <linux/export.h>
#include <linux/gfp.h>
-static void devm_clk_release(struct device *dev, void *res)
+static void my_clk_put(struct device *dev, void *res)
{
clk_put(*(struct clk **)res);
}
struct clk *devm_clk_get(struct device *dev, const char *id)
{
- struct clk **ptr, *clk;
-
- ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- clk = clk_get(dev, id);
- if (!IS_ERR(clk)) {
- *ptr = clk;
- devres_add(dev, ptr);
- } else {
- devres_free(ptr);
- }
+ int ret;
+ struct clk *clk = clk_get(dev, id);
+
+ if (IS_ERR(clk))
+ return clk;
+
+ ret = devm_add(dev, my_clk_put, &clk, sizeof(clk));
+ if (ret)
+ return ERR_PTR(ret);
return clk;
}
@@ -40,14 +36,14 @@ struct clk *devm_clk_get_optional(struct device *dev, const char *id)
}
EXPORT_SYMBOL(devm_clk_get_optional);
-struct clk_bulk_devres {
- struct clk_bulk_data *clks;
+struct clk_bulk_args {
int num_clks;
+ struct clk_bulk_data *clks;
};
-static void devm_clk_bulk_release(struct device *dev, void *res)
+static void my_clk_bulk_put(struct device *dev, void *res)
{
- struct clk_bulk_devres *devres = res;
+ struct clk_bulk_args *devres = res;
clk_bulk_put(devres->num_clks, devres->clks);
}
@@ -55,25 +51,17 @@ static void devm_clk_bulk_release(struct device *dev, void *res)
static int __devm_clk_bulk_get(struct device *dev, int num_clks,
struct clk_bulk_data *clks, bool optional)
{
- struct clk_bulk_devres *devres;
int ret;
- devres = devres_alloc(devm_clk_bulk_release,
- sizeof(*devres), GFP_KERNEL);
- if (!devres)
- return -ENOMEM;
-
if (optional)
ret = clk_bulk_get_optional(dev, num_clks, clks);
else
ret = clk_bulk_get(dev, num_clks, clks);
- if (!ret) {
- devres->clks = clks;
- devres->num_clks = num_clks;
- devres_add(dev, devres);
- } else {
- devres_free(devres);
- }
+
+ if (ret)
+ return ret;
+
+ ret = devm_vadd(dev, my_clk_bulk_put, clk_bulk_args, num_clks, clks);
return ret;
}
@@ -95,24 +83,15 @@ EXPORT_SYMBOL_GPL(devm_clk_bulk_get_optional);
int __must_check devm_clk_bulk_get_all(struct device *dev,
struct clk_bulk_data **clks)
{
- struct clk_bulk_devres *devres;
int ret;
+ int num_clks = clk_bulk_get_all(dev, clks);
- devres = devres_alloc(devm_clk_bulk_release,
- sizeof(*devres), GFP_KERNEL);
- if (!devres)
- return -ENOMEM;
-
- ret = clk_bulk_get_all(dev, &devres->clks);
- if (ret > 0) {
- *clks = devres->clks;
- devres->num_clks = ret;
- devres_add(dev, devres);
- } else {
- devres_free(devres);
- }
+ if (num_clks <= 0)
+ return num_clks;
- return ret;
+ ret = devm_vadd(dev, my_clk_bulk_put, clk_bulk_args, num_clks, *clks);
+
+ return ret ? : num_clks;
}
EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all);
@@ -128,30 +107,22 @@ static int devm_clk_match(struct device *dev, void *res, void *data)
void devm_clk_put(struct device *dev, struct clk *clk)
{
- int ret;
-
- ret = devres_release(dev, devm_clk_release, devm_clk_match, clk);
-
- WARN_ON(ret);
+ WARN_ON(devres_release(dev, my_clk_put, devm_clk_match, clk));
}
EXPORT_SYMBOL(devm_clk_put);
struct clk *devm_get_clk_from_child(struct device *dev,
struct device_node *np, const char *con_id)
{
- struct clk **ptr, *clk;
-
- ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- clk = of_clk_get_by_name(np, con_id);
- if (!IS_ERR(clk)) {
- *ptr = clk;
- devres_add(dev, ptr);
- } else {
- devres_free(ptr);
- }
+ int ret;
+ struct clk *clk = of_clk_get_by_name(np, con_id);
+
+ if (IS_ERR(clk))
+ return clk;
+
+ ret = devm_add(dev, my_clk_put, &clk, sizeof(clk));
+ if (ret)
+ return ERR_PTR(ret);
return clk;
}
diff --git a/include/linux/device.h b/include/linux/device.h
index e226030c1df3..9f18582fc0f9 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -969,6 +969,9 @@ void __iomem *devm_of_iomap(struct device *dev,
struct device_node *node, int index,
resource_size_t *size);
+int devm_add(struct device *dev, dr_release_t func, void *arg, size_t size);
+#define devm_vadd(dev, func, type, args...) \
+ devm_add(dev, func, &(struct type){args}, sizeof(struct type))
/* allows to add/remove a custom action to devres stack */
int devm_add_action(struct device *dev, void (*action)(void *), void *data);
void devm_remove_action(struct device *dev, void (*action)(void *), void *data);
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 2/8] powerpc/papr_scm: Provide support for fetching dimm health information
From: Vaibhav Jain @ 2020-02-20 9:57 UTC (permalink / raw)
To: linuxppc-dev
Cc: Vaibhav Jain, Michael Ellerman, Oliver O'Halloran,
Alastair D'Silva, Aneesh Kumar K . V
In-Reply-To: <20200220095805.197229-1-vaibhav@linux.ibm.com>
Implement support for fetching dimm health information via
H_SCM_HEALTH hcall as documented in Ref[1]. The hcall returns a pair of
64-bit big-endian integers which are then stored in 'struct
papr_scm_priv' and subsequently exposed to userspace via dimm
attribute 'papr_flags'.
'papr_flags' sysfs attribute reports space separated string flags
indicating various health state an nvdimm can be. These are:
* "not_armed" : Indicating that nvdimm contents wont survive a power
cycle.
* "save_fail" : Indicating that nvdimm contents couldn't be flushed
during last shutdown event.
* "restore_fail": Indicating that nvdimm contents couldn't be restored
during dimm initialization.
* "encrypted" : Dimm contents are encrypted.
* "smart_notify": There is health event for the nvdimm.
* "scrubbed" : Indicating that contents of the nvdimm have been
scrubbed.
* "locked" : Indicating that nvdimm contents cant be modified
until next power cycle.
[1]: commit 58b278f568f0 ("powerpc: Provide initial documentation for
PAPR hcalls")
Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>
---
arch/powerpc/platforms/pseries/papr_scm.c | 105 +++++++++++++++++++++-
1 file changed, 103 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c
index 0b4467e378e5..aaf2e4ab1f75 100644
--- a/arch/powerpc/platforms/pseries/papr_scm.c
+++ b/arch/powerpc/platforms/pseries/papr_scm.c
@@ -14,6 +14,7 @@
#include <linux/delay.h>
#include <asm/plpar_wrappers.h>
+#include <asm/papr_scm.h>
#define BIND_ANY_ADDR (~0ul)
@@ -39,6 +40,13 @@ struct papr_scm_priv {
struct resource res;
struct nd_region *region;
struct nd_interleave_set nd_set;
+
+ /* Protect dimm data from concurrent access */
+ struct mutex dimm_mutex;
+
+ /* Health information for the dimm */
+ __be64 health_bitmap;
+ __be64 health_bitmap_valid;
};
static int drc_pmem_bind(struct papr_scm_priv *p)
@@ -144,6 +152,35 @@ static int drc_pmem_query_n_bind(struct papr_scm_priv *p)
return drc_pmem_bind(p);
}
+static int drc_pmem_query_health(struct papr_scm_priv *p)
+{
+ unsigned long ret[PLPAR_HCALL_BUFSIZE];
+ int64_t rc;
+
+ rc = plpar_hcall(H_SCM_HEALTH, ret, p->drc_index);
+ if (rc != H_SUCCESS) {
+ dev_err(&p->pdev->dev,
+ "Failed to query health information, Err:%lld\n", rc);
+ return -ENXIO;
+ }
+
+ /* Protect modifications to papr_scm_priv with the mutex */
+ rc = mutex_lock_interruptible(&p->dimm_mutex);
+ if (rc)
+ return rc;
+
+ /* Store the retrieved health information in dimm platform data */
+ p->health_bitmap = ret[0];
+ p->health_bitmap_valid = ret[1];
+
+ dev_dbg(&p->pdev->dev,
+ "Queried dimm health info. Bitmap:0x%016llx Mask:0x%016llx\n",
+ be64_to_cpu(p->health_bitmap),
+ be64_to_cpu(p->health_bitmap_valid));
+
+ mutex_unlock(&p->dimm_mutex);
+ return 0;
+}
static int papr_scm_meta_get(struct papr_scm_priv *p,
struct nd_cmd_get_config_data_hdr *hdr)
@@ -304,6 +341,67 @@ static inline int papr_scm_node(int node)
return min_node;
}
+static ssize_t papr_flags_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm *dimm = to_nvdimm(dev);
+ struct papr_scm_priv *p = nvdimm_provider_data(dimm);
+ __be64 health;
+ int rc;
+
+ rc = drc_pmem_query_health(p);
+ if (rc)
+ return rc;
+
+ /* Protect against modifications to papr_scm_priv with the mutex */
+ rc = mutex_lock_interruptible(&p->dimm_mutex);
+ if (rc)
+ return rc;
+
+ health = p->health_bitmap & p->health_bitmap_valid;
+
+ /* Check for various masks in bitmap and set the buffer */
+ if (health & PAPR_SCM_DIMM_UNARMED_MASK)
+ rc += sprintf(buf, "not_armed ");
+
+ if (health & PAPR_SCM_DIMM_BAD_SHUTDOWN_MASK)
+ rc += sprintf(buf + rc, "save_fail ");
+
+ if (health & PAPR_SCM_DIMM_BAD_RESTORE_MASK)
+ rc += sprintf(buf + rc, "restore_fail ");
+
+ if (health & PAPR_SCM_DIMM_ENCRYPTED)
+ rc += sprintf(buf + rc, "encrypted ");
+
+ if (health & PAPR_SCM_DIMM_SMART_EVENT_MASK)
+ rc += sprintf(buf + rc, "smart_notify ");
+
+ if (health & PAPR_SCM_DIMM_SCRUBBED_AND_LOCKED)
+ rc += sprintf(buf + rc, "scrubbed locked ");
+
+ if (rc > 0)
+ rc += sprintf(buf + rc, "\n");
+
+ mutex_unlock(&p->dimm_mutex);
+ return rc;
+}
+DEVICE_ATTR_RO(papr_flags);
+
+/* papr_scm specific dimm attributes */
+static struct attribute *papr_scm_nd_attributes[] = {
+ &dev_attr_papr_flags.attr,
+ NULL,
+};
+
+static struct attribute_group papr_scm_nd_attribute_group = {
+ .attrs = papr_scm_nd_attributes,
+};
+
+static const struct attribute_group *papr_scm_dimm_attr_groups[] = {
+ &papr_scm_nd_attribute_group,
+ NULL,
+};
+
static int papr_scm_nvdimm_init(struct papr_scm_priv *p)
{
struct device *dev = &p->pdev->dev;
@@ -330,8 +428,8 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p)
dimm_flags = 0;
set_bit(NDD_ALIASING, &dimm_flags);
- p->nvdimm = nvdimm_create(p->bus, p, NULL, dimm_flags,
- PAPR_SCM_DIMM_CMD_MASK, 0, NULL);
+ p->nvdimm = nvdimm_create(p->bus, p, papr_scm_dimm_attr_groups,
+ dimm_flags, PAPR_SCM_DIMM_CMD_MASK, 0, NULL);
if (!p->nvdimm) {
dev_err(dev, "Error creating DIMM object for %pOF\n", p->dn);
goto err;
@@ -415,6 +513,9 @@ static int papr_scm_probe(struct platform_device *pdev)
if (!p)
return -ENOMEM;
+ /* Initialize the dimm mutex */
+ mutex_init(&p->dimm_mutex);
+
/* optional DT properties */
of_property_read_u32(dn, "ibm,metadata-size", &metadata_size);
--
2.24.1
^ permalink raw reply related
* Re: [PATCH v2 2/8] blk-mq: Keep set->nr_hw_queues and set->map[].nr_queues in sync
From: Ming Lei @ 2020-02-20 10:05 UTC (permalink / raw)
To: Bart Van Assche
Cc: Jens Axboe, linux-block, Christoph Hellwig, Christoph Hellwig,
Hannes Reinecke, Johannes Thumshirn, syzbot+d44e1b26ce5c3e77458d
In-Reply-To: <20200220024441.11558-3-bvanassche@acm.org>
On Wed, Feb 19, 2020 at 06:44:35PM -0800, Bart Van Assche wrote:
> blk_mq_map_queues() and multiple .map_queues() implementations expect that
> set->map[HCTX_TYPE_DEFAULT].nr_queues is set to the number of hardware
Only single queue mapping expects set->map[HCTX_TYPE_DEFAULT].nr_queues
to be set->nr_hw_queues. For multiple mapping, set->nr_hw_queues should
be sum of each mapping's nr_queue.
Thanks,
Ming
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.