* [PATCH 2/3] pwm: lpc18xx-sct: Reduce number of devm memory allocations
2021-11-10 8:49 [PATCH 1/3] pwm: lpc18xx-sct: Initialize driver data and hardware before pwmchip_add() Uwe Kleine-König
@ 2021-11-10 8:49 ` Uwe Kleine-König
2022-02-01 7:51 ` Thierry Reding
2021-11-10 8:49 ` [PATCH 3/3] pwm: lpc18xx-sct: Simplify driver by not using pwm_[gs]et_chip_data() Uwe Kleine-König
2022-02-01 7:47 ` [PATCH 1/3] pwm: lpc18xx-sct: Initialize driver data and hardware before pwmchip_add() Thierry Reding
2 siblings, 1 reply; 6+ messages in thread
From: Uwe Kleine-König @ 2021-11-10 8:49 UTC (permalink / raw)
To: Thierry Reding, Lee Jones, Vladimir Zapolskiy; +Cc: linux-pwm, kernel
Each devm allocations has an overhead of 24 bytes to store the related
struct devres_node additionally to the fragmentation of the allocator.
So allocating 16 struct lpc18xx_pwm_data (which only hold a single int)
adds quite some overhead. Instead put the per-channel data into the
driver data struct and allocate it in one go.
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
drivers/pwm/pwm-lpc18xx-sct.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c
index 8cc8ae16553c..6cf02554066c 100644
--- a/drivers/pwm/pwm-lpc18xx-sct.c
+++ b/drivers/pwm/pwm-lpc18xx-sct.c
@@ -76,6 +76,8 @@
#define LPC18XX_PWM_EVENT_PERIOD 0
#define LPC18XX_PWM_EVENT_MAX 16
+#define LPC18XX_NUM_PWMS 16
+
/* SCT conflict resolution */
enum lpc18xx_pwm_res_action {
LPC18XX_PWM_RES_NONE,
@@ -101,6 +103,7 @@ struct lpc18xx_pwm_chip {
unsigned long event_map;
struct mutex res_lock;
struct mutex period_lock;
+ struct lpc18xx_pwm_data channeldata[LPC18XX_NUM_PWMS];
};
static inline struct lpc18xx_pwm_chip *
@@ -370,7 +373,7 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev)
lpc18xx_pwm->chip.dev = &pdev->dev;
lpc18xx_pwm->chip.ops = &lpc18xx_pwm_ops;
- lpc18xx_pwm->chip.npwm = 16;
+ lpc18xx_pwm->chip.npwm = LPC18XX_NUM_PWMS;
/* SCT counter must be in unify (32 bit) mode */
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CONFIG,
@@ -400,12 +403,7 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev)
pwm = &lpc18xx_pwm->chip.pwms[i];
- data = devm_kzalloc(lpc18xx_pwm->dev, sizeof(*data),
- GFP_KERNEL);
- if (!data) {
- ret = -ENOMEM;
- goto disable_pwmclk;
- }
+ data = &lpc18xx_pwm->channeldata[i];
pwm_set_chip_data(pwm, data);
}
--
2.30.2
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 3/3] pwm: lpc18xx-sct: Simplify driver by not using pwm_[gs]et_chip_data()
2021-11-10 8:49 [PATCH 1/3] pwm: lpc18xx-sct: Initialize driver data and hardware before pwmchip_add() Uwe Kleine-König
2021-11-10 8:49 ` [PATCH 2/3] pwm: lpc18xx-sct: Reduce number of devm memory allocations Uwe Kleine-König
@ 2021-11-10 8:49 ` Uwe Kleine-König
2022-02-01 7:47 ` [PATCH 1/3] pwm: lpc18xx-sct: Initialize driver data and hardware before pwmchip_add() Thierry Reding
2 siblings, 0 replies; 6+ messages in thread
From: Uwe Kleine-König @ 2021-11-10 8:49 UTC (permalink / raw)
To: Thierry Reding, Lee Jones, Vladimir Zapolskiy; +Cc: linux-pwm, kernel
The per-channel data is available directly in the driver data struct. So
use it without making use of pwm_[gs]et_chip_data().
The relevant change introduced by this patch to lpc18xx_pwm_disable() at
the assembler level (for an arm lpc18xx_defconfig build) is:
push {r3, r4, r5, lr}
mov r4, r0
mov r0, r1
mov r5, r1
bl 0 <pwm_get_chip_data>
ldr r3, [r0, #0]
changes to
ldr r3, [r1, #8]
push {r4, lr}
add.w r3, r0, r3, lsl #2
ldr r3, [r3, #92] ; 0x5c
So this reduces stack usage, has an improved runtime behavior because of
better pipeline usage, doesn't branch to an external function and the
generated code is a bit smaller occupying less memory.
The codesize of lpc18xx_pwm_probe() is reduced by 32 bytes.
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
drivers/pwm/pwm-lpc18xx-sct.c | 23 ++++++-----------------
1 file changed, 6 insertions(+), 17 deletions(-)
diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c
index 6cf02554066c..b909096dba2f 100644
--- a/drivers/pwm/pwm-lpc18xx-sct.c
+++ b/drivers/pwm/pwm-lpc18xx-sct.c
@@ -166,7 +166,7 @@ static void lpc18xx_pwm_config_duty(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
- struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
+ struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
u64 val;
val = (u64)duty_ns * lpc18xx_pwm->clk_rate;
@@ -236,7 +236,7 @@ static int lpc18xx_pwm_set_polarity(struct pwm_chip *chip,
static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
- struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
+ struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
enum lpc18xx_pwm_res_action res_action;
unsigned int set_event, clear_event;
@@ -271,7 +271,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
static void lpc18xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
- struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
+ struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
lpc18xx_pwm_writel(lpc18xx_pwm,
LPC18XX_PWM_EVCTRL(lpc18xx_data->duty_event), 0);
@@ -282,7 +282,7 @@ static void lpc18xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
- struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
+ struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
unsigned long event;
event = find_first_zero_bit(&lpc18xx_pwm->event_map,
@@ -303,7 +303,7 @@ static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
static void lpc18xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
- struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
+ struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
clear_bit(lpc18xx_data->duty_event, &lpc18xx_pwm->event_map);
}
@@ -327,8 +327,7 @@ MODULE_DEVICE_TABLE(of, lpc18xx_pwm_of_match);
static int lpc18xx_pwm_probe(struct platform_device *pdev)
{
struct lpc18xx_pwm_chip *lpc18xx_pwm;
- struct pwm_device *pwm;
- int ret, i;
+ int ret;
u64 val;
lpc18xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*lpc18xx_pwm),
@@ -398,16 +397,6 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev)
lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_LIMIT,
BIT(lpc18xx_pwm->period_event));
- for (i = 0; i < lpc18xx_pwm->chip.npwm; i++) {
- struct lpc18xx_pwm_data *data;
-
- pwm = &lpc18xx_pwm->chip.pwms[i];
-
- data = &lpc18xx_pwm->channeldata[i];
-
- pwm_set_chip_data(pwm, data);
- }
-
val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL);
val &= ~LPC18XX_PWM_BIDIR;
val &= ~LPC18XX_PWM_CTRL_HALT;
--
2.30.2
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH 1/3] pwm: lpc18xx-sct: Initialize driver data and hardware before pwmchip_add()
2021-11-10 8:49 [PATCH 1/3] pwm: lpc18xx-sct: Initialize driver data and hardware before pwmchip_add() Uwe Kleine-König
2021-11-10 8:49 ` [PATCH 2/3] pwm: lpc18xx-sct: Reduce number of devm memory allocations Uwe Kleine-König
2021-11-10 8:49 ` [PATCH 3/3] pwm: lpc18xx-sct: Simplify driver by not using pwm_[gs]et_chip_data() Uwe Kleine-König
@ 2022-02-01 7:47 ` Thierry Reding
2022-02-01 8:19 ` Uwe Kleine-König
2 siblings, 1 reply; 6+ messages in thread
From: Thierry Reding @ 2022-02-01 7:47 UTC (permalink / raw)
To: Uwe Kleine-König; +Cc: Lee Jones, Vladimir Zapolskiy, linux-pwm, kernel
[-- Attachment #1: Type: text/plain, Size: 2617 bytes --]
On Wed, Nov 10, 2021 at 09:49:48AM +0100, Uwe Kleine-König wrote:
> When a driver calls pwmchip_add() it has to be prepared to immediately
> get its callbacks called. So move allocation of driver data and hardware
> initialization before the call to pwmchip_add().
>
> This fixes a potential NULL pointer exception and a race condition on
> register writes.
>
> Fixes: 841e6f90bb78 ("pwm: NXP LPC18xx PWM/SCT driver")
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> ---
> drivers/pwm/pwm-lpc18xx-sct.c | 20 +++++++++-----------
> 1 file changed, 9 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c
> index 8e461f3baa05..8cc8ae16553c 100644
> --- a/drivers/pwm/pwm-lpc18xx-sct.c
> +++ b/drivers/pwm/pwm-lpc18xx-sct.c
> @@ -395,12 +395,6 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev)
> lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_LIMIT,
> BIT(lpc18xx_pwm->period_event));
>
> - ret = pwmchip_add(&lpc18xx_pwm->chip);
> - if (ret < 0) {
> - dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret);
> - goto disable_pwmclk;
> - }
> -
> for (i = 0; i < lpc18xx_pwm->chip.npwm; i++) {
> struct lpc18xx_pwm_data *data;
>
> @@ -410,14 +404,12 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev)
> GFP_KERNEL);
> if (!data) {
> ret = -ENOMEM;
> - goto remove_pwmchip;
> + goto disable_pwmclk;
> }
>
> pwm_set_chip_data(pwm, data);
> }
>
> - platform_set_drvdata(pdev, lpc18xx_pwm);
> -
> val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL);
> val &= ~LPC18XX_PWM_BIDIR;
> val &= ~LPC18XX_PWM_CTRL_HALT;
> @@ -425,10 +417,16 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev)
> val |= LPC18XX_PWM_PRE(0);
> lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, val);
>
> + ret = pwmchip_add(&lpc18xx_pwm->chip);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret);
> + goto disable_pwmclk;
> + }
> +
> + platform_set_drvdata(pdev, lpc18xx_pwm);
Is there any particular reason why this needs to move? The driver only
call platform_get_drvdata() from the ->remove() implementation at this
point, so this should be fine, but it's possible that it would at some
future point try to access this data from some code path that may get
called as part of pwmchip_add(), so doing this prior to chip addition
may be a better option.
No need to resend, I can fix that up as I apply if there are no strong
reasons to keep this after pwmchip_add().
Thierry
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread