From a6143bdcaf7e37ecce05df2a3d3c1c5d587f87b1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 19 Jun 2024 12:11:42 +0200 Subject: mfd: stm32-timers: Unify alignment of register definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use tabs consistently for indention and properly align register names, values and comments. This improves readability (at least for my eyes). Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/da3b7f9af5794d7463aa62cbaa7251abf1af2018.1718791090.git.u.kleine-koenig@baylibre.com Signed-off-by: Lee Jones --- include/linux/mfd/stm32-timers.h | 170 +++++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h index 9eb17481b07f..5794110b2b28 100644 --- a/include/linux/mfd/stm32-timers.h +++ b/include/linux/mfd/stm32-timers.h @@ -12,97 +12,97 @@ #include #include -#define TIM_CR1 0x00 /* Control Register 1 */ -#define TIM_CR2 0x04 /* Control Register 2 */ -#define TIM_SMCR 0x08 /* Slave mode control reg */ -#define TIM_DIER 0x0C /* DMA/interrupt register */ -#define TIM_SR 0x10 /* Status register */ -#define TIM_EGR 0x14 /* Event Generation Reg */ -#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */ -#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */ -#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */ -#define TIM_CNT 0x24 /* Counter */ -#define TIM_PSC 0x28 /* Prescaler */ -#define TIM_ARR 0x2c /* Auto-Reload Register */ -#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */ -#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */ -#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */ -#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */ -#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */ -#define TIM_DCR 0x48 /* DMA control register */ -#define TIM_DMAR 0x4C /* DMA register for transfer */ -#define TIM_TISEL 0x68 /* Input Selection */ +#define TIM_CR1 0x00 /* Control Register 1 */ +#define TIM_CR2 0x04 /* Control Register 2 */ +#define TIM_SMCR 0x08 /* Slave mode control reg */ +#define TIM_DIER 0x0C /* DMA/interrupt register */ +#define TIM_SR 0x10 /* Status register */ +#define TIM_EGR 0x14 /* Event Generation Reg */ +#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */ +#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */ +#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */ +#define TIM_CNT 0x24 /* Counter */ +#define TIM_PSC 0x28 /* Prescaler */ +#define TIM_ARR 0x2c /* Auto-Reload Register */ +#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */ +#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */ +#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */ +#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */ +#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */ +#define TIM_DCR 0x48 /* DMA control register */ +#define TIM_DMAR 0x4C /* DMA register for transfer */ +#define TIM_TISEL 0x68 /* Input Selection */ -#define TIM_CR1_CEN BIT(0) /* Counter Enable */ -#define TIM_CR1_DIR BIT(4) /* Counter Direction */ -#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */ -#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */ -#define TIM_CR2_MMS2 GENMASK(23, 20) /* Master mode selection 2 */ -#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */ -#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */ -#define TIM_DIER_UIE BIT(0) /* Update interrupt */ -#define TIM_DIER_CC1IE BIT(1) /* CC1 Interrupt Enable */ -#define TIM_DIER_CC2IE BIT(2) /* CC2 Interrupt Enable */ -#define TIM_DIER_CC3IE BIT(3) /* CC3 Interrupt Enable */ -#define TIM_DIER_CC4IE BIT(4) /* CC4 Interrupt Enable */ -#define TIM_DIER_CC_IE(x) BIT((x) + 1) /* CC1, CC2, CC3, CC4 interrupt enable */ -#define TIM_DIER_UDE BIT(8) /* Update DMA request Enable */ -#define TIM_DIER_CC1DE BIT(9) /* CC1 DMA request Enable */ -#define TIM_DIER_CC2DE BIT(10) /* CC2 DMA request Enable */ -#define TIM_DIER_CC3DE BIT(11) /* CC3 DMA request Enable */ -#define TIM_DIER_CC4DE BIT(12) /* CC4 DMA request Enable */ -#define TIM_DIER_COMDE BIT(13) /* COM DMA request Enable */ -#define TIM_DIER_TDE BIT(14) /* Trigger DMA request Enable */ -#define TIM_SR_UIF BIT(0) /* Update interrupt flag */ -#define TIM_SR_CC_IF(x) BIT((x) + 1) /* CC1, CC2, CC3, CC4 interrupt flag */ -#define TIM_EGR_UG BIT(0) /* Update Generation */ -#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */ -#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */ -#define TIM_CCMR_CC1S (BIT(0) | BIT(1)) /* Capture/compare 1 sel */ -#define TIM_CCMR_IC1PSC GENMASK(3, 2) /* Input capture 1 prescaler */ -#define TIM_CCMR_CC2S (BIT(8) | BIT(9)) /* Capture/compare 2 sel */ -#define TIM_CCMR_IC2PSC GENMASK(11, 10) /* Input capture 2 prescaler */ -#define TIM_CCMR_CC1S_TI1 BIT(0) /* IC1/IC3 selects TI1/TI3 */ -#define TIM_CCMR_CC1S_TI2 BIT(1) /* IC1/IC3 selects TI2/TI4 */ -#define TIM_CCMR_CC2S_TI2 BIT(8) /* IC2/IC4 selects TI2/TI4 */ -#define TIM_CCMR_CC2S_TI1 BIT(9) /* IC2/IC4 selects TI1/TI3 */ -#define TIM_CCMR_CC3S (BIT(0) | BIT(1)) /* Capture/compare 3 sel */ -#define TIM_CCMR_CC4S (BIT(8) | BIT(9)) /* Capture/compare 4 sel */ -#define TIM_CCMR_CC3S_TI3 BIT(0) /* IC3 selects TI3 */ -#define TIM_CCMR_CC4S_TI4 BIT(8) /* IC4 selects TI4 */ -#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */ -#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */ -#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */ -#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */ -#define TIM_CCER_CC2E BIT(4) /* Capt/Comp 2 out Ena */ -#define TIM_CCER_CC2P BIT(5) /* Capt/Comp 2 Polarity */ -#define TIM_CCER_CC2NP BIT(7) /* Capt/Comp 2N Polarity */ -#define TIM_CCER_CC3E BIT(8) /* Capt/Comp 3 out Ena */ -#define TIM_CCER_CC3P BIT(9) /* Capt/Comp 3 Polarity */ -#define TIM_CCER_CC3NP BIT(11) /* Capt/Comp 3N Polarity */ -#define TIM_CCER_CC4E BIT(12) /* Capt/Comp 4 out Ena */ -#define TIM_CCER_CC4P BIT(13) /* Capt/Comp 4 Polarity */ -#define TIM_CCER_CC4NP BIT(15) /* Capt/Comp 4N Polarity */ -#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12)) -#define TIM_BDTR_BKE(x) BIT(12 + (x) * 12) /* Break input enable */ -#define TIM_BDTR_BKP(x) BIT(13 + (x) * 12) /* Break input polarity */ -#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */ -#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */ -#define TIM_BDTR_BKF(x) (0xf << (16 + (x) * 4)) -#define TIM_DCR_DBA GENMASK(4, 0) /* DMA base addr */ -#define TIM_DCR_DBL GENMASK(12, 8) /* DMA burst len */ +#define TIM_CR1_CEN BIT(0) /* Counter Enable */ +#define TIM_CR1_DIR BIT(4) /* Counter Direction */ +#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */ +#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */ +#define TIM_CR2_MMS2 GENMASK(23, 20) /* Master mode selection 2 */ +#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */ +#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */ +#define TIM_DIER_UIE BIT(0) /* Update interrupt */ +#define TIM_DIER_CC1IE BIT(1) /* CC1 Interrupt Enable */ +#define TIM_DIER_CC2IE BIT(2) /* CC2 Interrupt Enable */ +#define TIM_DIER_CC3IE BIT(3) /* CC3 Interrupt Enable */ +#define TIM_DIER_CC4IE BIT(4) /* CC4 Interrupt Enable */ +#define TIM_DIER_CC_IE(x) BIT((x) + 1) /* CC1, CC2, CC3, CC4 interrupt enable */ +#define TIM_DIER_UDE BIT(8) /* Update DMA request Enable */ +#define TIM_DIER_CC1DE BIT(9) /* CC1 DMA request Enable */ +#define TIM_DIER_CC2DE BIT(10) /* CC2 DMA request Enable */ +#define TIM_DIER_CC3DE BIT(11) /* CC3 DMA request Enable */ +#define TIM_DIER_CC4DE BIT(12) /* CC4 DMA request Enable */ +#define TIM_DIER_COMDE BIT(13) /* COM DMA request Enable */ +#define TIM_DIER_TDE BIT(14) /* Trigger DMA request Enable */ +#define TIM_SR_UIF BIT(0) /* Update interrupt flag */ +#define TIM_SR_CC_IF(x) BIT((x) + 1) /* CC1, CC2, CC3, CC4 interrupt flag */ +#define TIM_EGR_UG BIT(0) /* Update Generation */ +#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */ +#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */ +#define TIM_CCMR_CC1S (BIT(0) | BIT(1)) /* Capture/compare 1 sel */ +#define TIM_CCMR_IC1PSC GENMASK(3, 2) /* Input capture 1 prescaler */ +#define TIM_CCMR_CC2S (BIT(8) | BIT(9)) /* Capture/compare 2 sel */ +#define TIM_CCMR_IC2PSC GENMASK(11, 10) /* Input capture 2 prescaler */ +#define TIM_CCMR_CC1S_TI1 BIT(0) /* IC1/IC3 selects TI1/TI3 */ +#define TIM_CCMR_CC1S_TI2 BIT(1) /* IC1/IC3 selects TI2/TI4 */ +#define TIM_CCMR_CC2S_TI2 BIT(8) /* IC2/IC4 selects TI2/TI4 */ +#define TIM_CCMR_CC2S_TI1 BIT(9) /* IC2/IC4 selects TI1/TI3 */ +#define TIM_CCMR_CC3S (BIT(0) | BIT(1)) /* Capture/compare 3 sel */ +#define TIM_CCMR_CC4S (BIT(8) | BIT(9)) /* Capture/compare 4 sel */ +#define TIM_CCMR_CC3S_TI3 BIT(0) /* IC3 selects TI3 */ +#define TIM_CCMR_CC4S_TI4 BIT(8) /* IC4 selects TI4 */ +#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */ +#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */ +#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */ +#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */ +#define TIM_CCER_CC2E BIT(4) /* Capt/Comp 2 out Ena */ +#define TIM_CCER_CC2P BIT(5) /* Capt/Comp 2 Polarity */ +#define TIM_CCER_CC2NP BIT(7) /* Capt/Comp 2N Polarity */ +#define TIM_CCER_CC3E BIT(8) /* Capt/Comp 3 out Ena */ +#define TIM_CCER_CC3P BIT(9) /* Capt/Comp 3 Polarity */ +#define TIM_CCER_CC3NP BIT(11) /* Capt/Comp 3N Polarity */ +#define TIM_CCER_CC4E BIT(12) /* Capt/Comp 4 out Ena */ +#define TIM_CCER_CC4P BIT(13) /* Capt/Comp 4 Polarity */ +#define TIM_CCER_CC4NP BIT(15) /* Capt/Comp 4N Polarity */ +#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12)) +#define TIM_BDTR_BKE(x) BIT(12 + (x) * 12) /* Break input enable */ +#define TIM_BDTR_BKP(x) BIT(13 + (x) * 12) /* Break input polarity */ +#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */ +#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */ +#define TIM_BDTR_BKF(x) (0xf << (16 + (x) * 4)) +#define TIM_DCR_DBA GENMASK(4, 0) /* DMA base addr */ +#define TIM_DCR_DBL GENMASK(12, 8) /* DMA burst len */ -#define MAX_TIM_PSC 0xFFFF -#define MAX_TIM_ICPSC 0x3 -#define TIM_CR2_MMS_SHIFT 4 -#define TIM_CR2_MMS2_SHIFT 20 +#define MAX_TIM_PSC 0xFFFF +#define MAX_TIM_ICPSC 0x3 +#define TIM_CR2_MMS_SHIFT 4 +#define TIM_CR2_MMS2_SHIFT 20 #define TIM_SMCR_SMS_SLAVE_MODE_DISABLED 0 /* counts on internal clock when CEN=1 */ #define TIM_SMCR_SMS_ENCODER_MODE_1 1 /* counts TI1FP1 edges, depending on TI2FP2 level */ #define TIM_SMCR_SMS_ENCODER_MODE_2 2 /* counts TI2FP2 edges, depending on TI1FP1 level */ #define TIM_SMCR_SMS_ENCODER_MODE_3 3 /* counts on both TI1FP1 and TI2FP2 edges */ -#define TIM_SMCR_TS_SHIFT 4 -#define TIM_BDTR_BKF_MASK 0xF -#define TIM_BDTR_BKF_SHIFT(x) (16 + (x) * 4) +#define TIM_SMCR_TS_SHIFT 4 +#define TIM_BDTR_BKF_MASK 0xF +#define TIM_BDTR_BKF_SHIFT(x) (16 + (x) * 4) enum stm32_timers_dmas { STM32_TIMERS_DMA_CH1, -- cgit v1.2.3-58-ga151 From 796b942f65967af55684bf520812787b97522151 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 19 Jun 2024 12:11:43 +0200 Subject: mfd: stm32-timers: Add some register definitions with a parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are some registers that belong together and are numbered from 1 to 4. Introduce a macro definition for these that takes the channel number as parameter and define the previously available constants using the new ones. This allows to simplify some users that up to now use constructs like TIM_CCER_CC1NE << (ch * 4) which is an ugly mix of using a predefined value and still knowing internal details about it. Note that there are several decrements by 1 involved. These are necessary because software guys start counting at 0 while the hardware designer started at 1 (and having TIM_CCER_CCxE(1) be TIM_CCER_CC2E isn't a sane option). The compiler is expected to optimize these out nicely. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/05df15f61dde81033407d3b4fcb67ee403ecc8db.1718791090.git.u.kleine-koenig@baylibre.com Signed-off-by: Lee Jones --- include/linux/mfd/stm32-timers.h | 60 +++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h index 5794110b2b28..92b45a559656 100644 --- a/include/linux/mfd/stm32-timers.h +++ b/include/linux/mfd/stm32-timers.h @@ -24,10 +24,11 @@ #define TIM_CNT 0x24 /* Counter */ #define TIM_PSC 0x28 /* Prescaler */ #define TIM_ARR 0x2c /* Auto-Reload Register */ -#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */ -#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */ -#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */ -#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */ +#define TIM_CCRx(x) (0x34 + 4 * ((x) - 1)) /* Capt/Comp Register x (x ∈ {1, .. 4}) */ +#define TIM_CCR1 TIM_CCRx(1) /* Capt/Comp Register 1 */ +#define TIM_CCR2 TIM_CCRx(2) /* Capt/Comp Register 2 */ +#define TIM_CCR3 TIM_CCRx(3) /* Capt/Comp Register 3 */ +#define TIM_CCR4 TIM_CCRx(4) /* Capt/Comp Register 4 */ #define TIM_BDTR 0x44 /* Break and Dead-Time Reg */ #define TIM_DCR 0x48 /* DMA control register */ #define TIM_DMAR 0x4C /* DMA register for transfer */ @@ -41,16 +42,18 @@ #define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */ #define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */ #define TIM_DIER_UIE BIT(0) /* Update interrupt */ -#define TIM_DIER_CC1IE BIT(1) /* CC1 Interrupt Enable */ -#define TIM_DIER_CC2IE BIT(2) /* CC2 Interrupt Enable */ -#define TIM_DIER_CC3IE BIT(3) /* CC3 Interrupt Enable */ -#define TIM_DIER_CC4IE BIT(4) /* CC4 Interrupt Enable */ +#define TIM_DIER_CCxIE(x) BIT(1 + ((x) - 1)) /* CCx Interrupt Enable (x ∈ {1, .. 4}) */ +#define TIM_DIER_CC1IE TIM_DIER_CCxIE(1) /* CC1 Interrupt Enable */ +#define TIM_DIER_CC2IE TIM_DIER_CCxIE(2) /* CC2 Interrupt Enable */ +#define TIM_DIER_CC3IE TIM_DIER_CCxIE(3) /* CC3 Interrupt Enable */ +#define TIM_DIER_CC4IE TIM_DIER_CCxIE(4) /* CC4 Interrupt Enable */ #define TIM_DIER_CC_IE(x) BIT((x) + 1) /* CC1, CC2, CC3, CC4 interrupt enable */ #define TIM_DIER_UDE BIT(8) /* Update DMA request Enable */ -#define TIM_DIER_CC1DE BIT(9) /* CC1 DMA request Enable */ -#define TIM_DIER_CC2DE BIT(10) /* CC2 DMA request Enable */ -#define TIM_DIER_CC3DE BIT(11) /* CC3 DMA request Enable */ -#define TIM_DIER_CC4DE BIT(12) /* CC4 DMA request Enable */ +#define TIM_DIER_CCxDE(x) BIT(9 + ((x) - 1)) /* CCx DMA request Enable (x ∈ {1, .. 4}) */ +#define TIM_DIER_CC1DE TIM_DIER_CCxDE(1) /* CC1 DMA request Enable */ +#define TIM_DIER_CC2DE TIM_DIER_CCxDE(2) /* CC2 DMA request Enable */ +#define TIM_DIER_CC3DE TIM_DIER_CCxDE(3) /* CC3 DMA request Enable */ +#define TIM_DIER_CC4DE TIM_DIER_CCxDE(4) /* CC4 DMA request Enable */ #define TIM_DIER_COMDE BIT(13) /* COM DMA request Enable */ #define TIM_DIER_TDE BIT(14) /* Trigger DMA request Enable */ #define TIM_SR_UIF BIT(0) /* Update interrupt flag */ @@ -70,19 +73,26 @@ #define TIM_CCMR_CC4S (BIT(8) | BIT(9)) /* Capture/compare 4 sel */ #define TIM_CCMR_CC3S_TI3 BIT(0) /* IC3 selects TI3 */ #define TIM_CCMR_CC4S_TI4 BIT(8) /* IC4 selects TI4 */ -#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */ -#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */ -#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */ -#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */ -#define TIM_CCER_CC2E BIT(4) /* Capt/Comp 2 out Ena */ -#define TIM_CCER_CC2P BIT(5) /* Capt/Comp 2 Polarity */ -#define TIM_CCER_CC2NP BIT(7) /* Capt/Comp 2N Polarity */ -#define TIM_CCER_CC3E BIT(8) /* Capt/Comp 3 out Ena */ -#define TIM_CCER_CC3P BIT(9) /* Capt/Comp 3 Polarity */ -#define TIM_CCER_CC3NP BIT(11) /* Capt/Comp 3N Polarity */ -#define TIM_CCER_CC4E BIT(12) /* Capt/Comp 4 out Ena */ -#define TIM_CCER_CC4P BIT(13) /* Capt/Comp 4 Polarity */ -#define TIM_CCER_CC4NP BIT(15) /* Capt/Comp 4N Polarity */ +#define TIM_CCER_CCxE(x) BIT(0 + 4 * ((x) - 1)) /* Capt/Comp x out Ena (x ∈ {1, .. 4}) */ +#define TIM_CCER_CCxP(x) BIT(1 + 4 * ((x) - 1)) /* Capt/Comp x Polarity (x ∈ {1, .. 4}) */ +#define TIM_CCER_CCxNE(x) BIT(2 + 4 * ((x) - 1)) /* Capt/Comp xN out Ena (x ∈ {1, .. 4}) */ +#define TIM_CCER_CCxNP(x) BIT(3 + 4 * ((x) - 1)) /* Capt/Comp xN Polarity (x ∈ {1, .. 4}) */ +#define TIM_CCER_CC1E TIM_CCER_CCxE(1) /* Capt/Comp 1 out Ena */ +#define TIM_CCER_CC1P TIM_CCER_CCxP(1) /* Capt/Comp 1 Polarity */ +#define TIM_CCER_CC1NE TIM_CCER_CCxNE(1) /* Capt/Comp 1N out Ena */ +#define TIM_CCER_CC1NP TIM_CCER_CCxNP(1) /* Capt/Comp 1N Polarity */ +#define TIM_CCER_CC2E TIM_CCER_CCxE(2) /* Capt/Comp 2 out Ena */ +#define TIM_CCER_CC2P TIM_CCER_CCxP(2) /* Capt/Comp 2 Polarity */ +#define TIM_CCER_CC2NE TIM_CCER_CCxNE(2) /* Capt/Comp 2N out Ena */ +#define TIM_CCER_CC2NP TIM_CCER_CCxNP(2) /* Capt/Comp 2N Polarity */ +#define TIM_CCER_CC3E TIM_CCER_CCxE(3) /* Capt/Comp 3 out Ena */ +#define TIM_CCER_CC3P TIM_CCER_CCxP(3) /* Capt/Comp 3 Polarity */ +#define TIM_CCER_CC3NE TIM_CCER_CCxNE(3) /* Capt/Comp 3N out Ena */ +#define TIM_CCER_CC3NP TIM_CCER_CCxNP(3) /* Capt/Comp 3N Polarity */ +#define TIM_CCER_CC4E TIM_CCER_CCxE(4) /* Capt/Comp 4 out Ena */ +#define TIM_CCER_CC4P TIM_CCER_CCxP(4) /* Capt/Comp 4 Polarity */ +#define TIM_CCER_CC4NE TIM_CCER_CCxNE(4) /* Capt/Comp 4N out Ena */ +#define TIM_CCER_CC4NP TIM_CCER_CCxNP(4) /* Capt/Comp 4N Polarity */ #define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12)) #define TIM_BDTR_BKE(x) BIT(12 + (x) * 12) /* Break input enable */ #define TIM_BDTR_BKP(x) BIT(13 + (x) * 12) /* Break input polarity */ -- cgit v1.2.3-58-ga151 From 9d7809aaea0fb569a9707975d37a170dc0111761 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 19 Jun 2024 12:11:44 +0200 Subject: counter: stm32-timer-cnt: Use TIM_DIER_CCxIE(x) instead of TIM_DIER_CCxIE(x) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These two defines have the same purpose and this change doesn't introduce any differences in drivers/counter/stm32-timer-cnt.o. The only difference between the two is that TIM_DIER_CC_IE(1) == TIM_DIER_CC2IE while TIM_DIER_CCxIE(1) == TIM_DIER_CC1IE . That makes it necessary to have an explicit "+ 1" in the user code, but IMHO this is a good thing as this is the code locatation that "knows" that for software channel 1 you have to use TIM_DIER_CC2IE (because software guys start counting at 0, while the relevant hardware designer started at 1). Signed-off-by: Uwe Kleine-König Acked-by: William Breathitt Gray Link: https://lore.kernel.org/r/126bd153a03f39e42645573eecf44ffab5354fc7.1718791090.git.u.kleine-koenig@baylibre.com Signed-off-by: Lee Jones --- drivers/counter/stm32-timer-cnt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 0664ef969f79..186e73d6ccb4 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -465,7 +465,7 @@ static int stm32_count_events_configure(struct counter_device *counter) ret = stm32_count_capture_configure(counter, event_node->channel, true); if (ret) return ret; - dier |= TIM_DIER_CC_IE(event_node->channel); + dier |= TIM_DIER_CCxIE(event_node->channel + 1); break; default: /* should never reach this path */ @@ -478,7 +478,7 @@ static int stm32_count_events_configure(struct counter_device *counter) /* check for disabled capture events */ for (i = 0 ; i < priv->nchannels; i++) { - if (!(dier & TIM_DIER_CC_IE(i))) { + if (!(dier & TIM_DIER_CCxIE(i + 1))) { ret = stm32_count_capture_configure(counter, i, false); if (ret) return ret; -- cgit v1.2.3-58-ga151 From 304d02aa711369da89b4f8c01702bf1b5d1f7abc Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 19 Jun 2024 12:11:45 +0200 Subject: mfd: stm32-timers: Drop unused TIM_DIER_CC_IE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This macro is misleading as TIM_DIER_CC_IE(1) == TIM_DIER_CC2IE . The only user was updated to use TIM_DIER_CCxIE() instead which doesn't suffer from this mismatch, so TIM_DIER_CC_IE can be dropped. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/6c8fcc4ed159992a1dbb0796087e6ceb10c39c96.1718791090.git.u.kleine-koenig@baylibre.com Signed-off-by: Lee Jones --- include/linux/mfd/stm32-timers.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h index 92b45a559656..f09ba598c97a 100644 --- a/include/linux/mfd/stm32-timers.h +++ b/include/linux/mfd/stm32-timers.h @@ -47,7 +47,6 @@ #define TIM_DIER_CC2IE TIM_DIER_CCxIE(2) /* CC2 Interrupt Enable */ #define TIM_DIER_CC3IE TIM_DIER_CCxIE(3) /* CC3 Interrupt Enable */ #define TIM_DIER_CC4IE TIM_DIER_CCxIE(4) /* CC4 Interrupt Enable */ -#define TIM_DIER_CC_IE(x) BIT((x) + 1) /* CC1, CC2, CC3, CC4 interrupt enable */ #define TIM_DIER_UDE BIT(8) /* Update DMA request Enable */ #define TIM_DIER_CCxDE(x) BIT(9 + ((x) - 1)) /* CCx DMA request Enable (x ∈ {1, .. 4}) */ #define TIM_DIER_CC1DE TIM_DIER_CCxDE(1) /* CC1 DMA request Enable */ -- cgit v1.2.3-58-ga151 From 7346e7a058a2c9aa9ff1cc699c7bf18a402d9f84 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 3 Jul 2024 13:00:06 +0200 Subject: pwm: stm32: Always do lazy disabling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the state changes from enabled to disabled, polarity, duty_cycle and period are not configured in hardware and TIM_CCER_CCxE is just cleared. However if the state changes from one disabled state to another, all parameters are written to hardware because the early exit from stm32_pwm_apply() is only taken if the pwm is currently enabled. This yields surprises like: Applying { .period = 1, .duty_cycle = 0, .enabled = false } succeeds if the pwm is initially on, but fails if it's already off because 1 is a too small period. Update the check for lazy disable to always exit early if the target state is disabled, no matter what is currently configured. Fixes: 7edf7369205b ("pwm: Add driver for STM32 plaftorm") Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20240703110010.672654-2-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-stm32.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 8bae3fd2b330..c586029caf23 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -452,8 +452,9 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, enabled = pwm->state.enabled; - if (enabled && !state->enabled) { - stm32_pwm_disable(priv, pwm->hwpwm); + if (!state->enabled) { + if (enabled) + stm32_pwm_disable(priv, pwm->hwpwm); return 0; } -- cgit v1.2.3-58-ga151 From 32207e9ddf7400a5676cb27559560cadee062577 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Fri, 17 May 2024 18:38:46 +0100 Subject: dt-bindings: pwm: describe the cells in #pwm-cells in pwm.yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When commit 89650a1e3b6f ("dt-bindings: pwm: Convert PWM bindings to json-schema") converted the pwm provider section of the text binding to dt-schema it also updated all references to pwm.txt in pwm provider bindings to pwm.yaml. Most pwm provider bindings had a reference to pwm.txt as it contains a description of what the cells in #pwm-cells are, albeit in the consumer section of the document. Only information in the provider section of the document was moved to the yaml binding, and it contains no information about the cell format, making all references to it for the cell format unhelpful. Fixes: 89650a1e3b6f ("dt-bindings: pwm: Convert PWM bindings to json-schema") Signed-off-by: Conor Dooley Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20240517-patient-stingily-30611f73e792@spud Signed-off-by: Uwe Kleine-König --- Documentation/devicetree/bindings/pwm/pwm.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pwm/pwm.yaml b/Documentation/devicetree/bindings/pwm/pwm.yaml index abd9fa873354..f2206ec3c7c4 100644 --- a/Documentation/devicetree/bindings/pwm/pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/pwm.yaml @@ -16,8 +16,10 @@ properties: pattern: "^pwm(@.*|-([0-9]|[1-9][0-9]+))?$" "#pwm-cells": - description: - Number of cells in a PWM specifier. + description: | + Number of cells in a PWM specifier. Typically the cells represent, in + order: the chip-relative PWM number, the PWM period in nanoseconds and + optionally a number of flags (defined in ). required: - "#pwm-cells" -- cgit v1.2.3-58-ga151 From 37f7707077f5ea2515bf4b1dc7fad43f8e12993e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 9 Jul 2024 12:18:05 +0200 Subject: pwm: atmel-tcb: Fix race condition and convert to guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hardware only supports a single period length for both PWM outputs. So atmel_tcb_pwm_config() checks the configuration of the other output if it's compatible with the currently requested setting. The register values are then actually updated in atmel_tcb_pwm_enable(). To make this race free the lock must be held during the whole process, so grab the lock in .apply() instead of individually in atmel_tcb_pwm_disable() and atmel_tcb_pwm_enable() which then also covers atmel_tcb_pwm_config(). To simplify handling, use the guard helper to let the compiler care for unlocking. Otherwise unlocking would be more difficult as there is more than one exit path in atmel_tcb_pwm_apply(). Fixes: 9421bade0765 ("pwm: atmel: add Timer Counter Block PWM driver") Signed-off-by: Uwe Kleine-König Acked-by: Nicolas Ferre Link: https://lore.kernel.org/r/20240709101806.52394-3-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-atmel-tcb.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 528e54c5999d..aca11493239a 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -81,7 +81,8 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip, tcbpwm->period = 0; tcbpwm->div = 0; - spin_lock(&tcbpwmc->lock); + guard(spinlock)(&tcbpwmc->lock); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); /* * Get init config from Timer Counter registers if @@ -107,7 +108,6 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip, cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0; regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr); - spin_unlock(&tcbpwmc->lock); return 0; } @@ -137,7 +137,6 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm, if (tcbpwm->duty == 0) polarity = !polarity; - spin_lock(&tcbpwmc->lock); regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); /* flush old setting and set the new one */ @@ -172,8 +171,6 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm, ATMEL_TC_SWTRG); tcbpwmc->bkup.enabled = 0; } - - spin_unlock(&tcbpwmc->lock); } static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, @@ -194,7 +191,6 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, if (tcbpwm->duty == 0) polarity = !polarity; - spin_lock(&tcbpwmc->lock); regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); /* flush old setting and set the new one */ @@ -256,7 +252,6 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR), ATMEL_TC_SWTRG | ATMEL_TC_CLKEN); tcbpwmc->bkup.enabled = 1; - spin_unlock(&tcbpwmc->lock); return 0; } @@ -341,9 +336,12 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); int duty_cycle, period; int ret; + guard(spinlock)(&tcbpwmc->lock); + if (!state->enabled) { atmel_tcb_pwm_disable(chip, pwm, state->polarity); return 0; -- cgit v1.2.3-58-ga151 From 307d0a70d029fa26c93c070341ba3acd6ba31db9 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 28 May 2024 16:20:24 -0400 Subject: dt-bindings: pwm: fsl-ftm: Convert to yaml format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert dt-bindings pwm-fsl-ftm.txt to yaml format. Additional change during convert: - "big-endian" is not required property. - Add "sleep" to pinctrl-names. - Change pinctrl-NNN to pinctrl-0 and pinctrl-1. - Remove label "pwm0" in example. Signed-off-by: Frank Li Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240528202025.2919358-1-Frank.Li@nxp.com Signed-off-by: Uwe Kleine-König --- .../devicetree/bindings/pwm/fsl,vf610-ftm-pwm.yaml | 92 ++++++++++++++++++++++ .../devicetree/bindings/pwm/pwm-fsl-ftm.txt | 55 ------------- 2 files changed, 92 insertions(+), 55 deletions(-) create mode 100644 Documentation/devicetree/bindings/pwm/fsl,vf610-ftm-pwm.yaml delete mode 100644 Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt diff --git a/Documentation/devicetree/bindings/pwm/fsl,vf610-ftm-pwm.yaml b/Documentation/devicetree/bindings/pwm/fsl,vf610-ftm-pwm.yaml new file mode 100644 index 000000000000..7f9f72d95e7a --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/fsl,vf610-ftm-pwm.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/fsl,vf610-ftm-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale FlexTimer Module (FTM) PWM controller + +description: | + The same FTM PWM device can have a different endianness on different SoCs. The + device tree provides a property to describing this so that an operating system + device driver can handle all variants of the device. Refer to the table below + for the endianness of the FTM PWM block as integrated into the existing SoCs: + + SoC | FTM-PWM endianness + --------+------------------- + Vybrid | LE + LS1 | BE + LS2 | LE + + Please see ../regmap/regmap.txt for more detail about how to specify endian + modes in device tree. + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - fsl,vf610-ftm-pwm + - fsl,imx8qm-ftm-pwm + + reg: + maxItems: 1 + + "#pwm-cells": + const: 3 + + clocks: + minItems: 4 + maxItems: 4 + + clock-names: + items: + - const: ftm_sys + - const: ftm_ext + - const: ftm_fix + - const: ftm_cnt_clk_en + + pinctrl-0: true + pinctrl-1: true + + pinctrl-names: + minItems: 1 + items: + - const: default + - const: sleep + + big-endian: + $ref: /schemas/types.yaml#/definitions/flag + description: + Boolean property, required if the FTM PWM registers use a big- + endian rather than little-endian layout. + +required: + - compatible + - reg + - clocks + - clock-names + +allOf: + - $ref: pwm.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + pwm@40038000 { + compatible = "fsl,vf610-ftm-pwm"; + reg = <0x40038000 0x1000>; + #pwm-cells = <3>; + clocks = <&clks VF610_CLK_FTM0>, + <&clks VF610_CLK_FTM0_EXT_SEL>, + <&clks VF610_CLK_FTM0_FIX_SEL>, + <&clks VF610_CLK_FTM0_EXT_FIX_EN>; + clock-names = "ftm_sys", "ftm_ext", "ftm_fix", "ftm_cnt_clk_en"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm0_1>; + big-endian; + }; diff --git a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt deleted file mode 100644 index 36532cd5ab25..000000000000 --- a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt +++ /dev/null @@ -1,55 +0,0 @@ -Freescale FlexTimer Module (FTM) PWM controller - -The same FTM PWM device can have a different endianness on different SoCs. The -device tree provides a property to describing this so that an operating system -device driver can handle all variants of the device. Refer to the table below -for the endianness of the FTM PWM block as integrated into the existing SoCs: - - SoC | FTM-PWM endianness - --------+------------------- - Vybrid | LE - LS1 | BE - LS2 | LE - -Please see ../regmap/regmap.txt for more detail about how to specify endian -modes in device tree. - - -Required properties: -- compatible : should be "fsl,-ftm-pwm" and one of the following - compatible strings: - - "fsl,vf610-ftm-pwm" for PWM compatible with the one integrated on VF610 - - "fsl,imx8qm-ftm-pwm" for PWM compatible with the one integrated on i.MX8QM -- reg: Physical base address and length of the controller's registers -- #pwm-cells: Should be 3. See pwm.yaml in this directory for a description of - the cells format. -- clock-names: Should include the following module clock source entries: - "ftm_sys" (module clock, also can be used as counter clock), - "ftm_ext" (external counter clock), - "ftm_fix" (fixed counter clock), - "ftm_cnt_clk_en" (external and fixed counter clock enable/disable). -- clocks: Must contain a phandle and clock specifier for each entry in - clock-names, please see clock/clock-bindings.txt for details of the property - values. -- pinctrl-names: Must contain a "default" entry. -- pinctrl-NNN: One property must exist for each entry in pinctrl-names. - See pinctrl/pinctrl-bindings.txt for details of the property values. -- big-endian: Boolean property, required if the FTM PWM registers use a big- - endian rather than little-endian layout. - -Example: - -pwm0: pwm@40038000 { - compatible = "fsl,vf610-ftm-pwm"; - reg = <0x40038000 0x1000>; - #pwm-cells = <3>; - clock-names = "ftm_sys", "ftm_ext", - "ftm_fix", "ftm_cnt_clk_en"; - clocks = <&clks VF610_CLK_FTM0>, - <&clks VF610_CLK_FTM0_EXT_SEL>, - <&clks VF610_CLK_FTM0_FIX_SEL>, - <&clks VF610_CLK_FTM0_EXT_FIX_EN>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pwm0_1>; - big-endian; -}; -- cgit v1.2.3-58-ga151 From 1edf2c2a2841f41e95287abe4b779840a17193d4 Mon Sep 17 00:00:00 2001 From: Drew Fustini Date: Wed, 5 Jun 2024 16:35:05 -0400 Subject: dt-bindings: pwm: Add AXI PWM generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Analog Devices AXI PWM generator. Link: https://analogdevicesinc.github.io/hdl/library/axi_pwm_gen/index.html Signed-off-by: Drew Fustini Reviewed-by: Krzysztof Kozlowski Acked-by: Michael Hennerich Acked-by: Nuno Sa Co-developed-by: Trevor Gamblin Signed-off-by: Trevor Gamblin Link: https://lore.kernel.org/r/20240605203507.1934434-2-tgamblin@baylibre.com Signed-off-by: Uwe Kleine-König --- .../devicetree/bindings/pwm/adi,axi-pwmgen.yaml | 48 ++++++++++++++++++++++ MAINTAINERS | 8 ++++ 2 files changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml diff --git a/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml b/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml new file mode 100644 index 000000000000..ec6115d3796b --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/adi,axi-pwmgen.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AXI PWM generator + +maintainers: + - Michael Hennerich + - Nuno Sá + +description: + The Analog Devices AXI PWM generator can generate PWM signals + with variable pulse width and period. + + https://wiki.analog.com/resources/fpga/docs/axi_pwm_gen + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: adi,axi-pwmgen-2.00.a + + reg: + maxItems: 1 + + "#pwm-cells": + const: 2 + + clocks: + maxItems: 1 + +required: + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + pwm@44b00000 { + compatible = "adi,axi-pwmgen-2.00.a"; + reg = <0x44b00000 0x1000>; + clocks = <&spi_clk>; + #pwm-cells = <2>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index d6c90161c7bf..09c61e0b614e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3571,6 +3571,14 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/spi/adi,axi-spi-engine.yaml F: drivers/spi/spi-axi-spi-engine.c +AXI PWM GENERATOR +M: Michael Hennerich +M: Nuno Sá +L: linux-pwm@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml + AXXIA I2C CONTROLLER M: Krzysztof Adamski L: linux-i2c@vger.kernel.org -- cgit v1.2.3-58-ga151 From 41814fe5c782bdf68c25bb99398d38619a2fb5e6 Mon Sep 17 00:00:00 2001 From: Drew Fustini Date: Wed, 5 Jun 2024 16:35:06 -0400 Subject: pwm: Add driver for AXI PWM generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the Analog Devices AXI PWM Generator. This device is an FPGA-implemented peripheral used as PWM signal generator and can be interfaced with AXI4. The register map of this peripheral makes it possible to configure the period and duty cycle of the output signal. Link: https://analogdevicesinc.github.io/hdl/library/axi_pwm_gen/index.html Co-developed-by: Sergiu Cuciurean Signed-off-by: Sergiu Cuciurean Co-developed-by: David Lechner Signed-off-by: David Lechner Signed-off-by: Drew Fustini Acked-by: Nuno Sa Co-developed-by: Trevor Gamblin Signed-off-by: Trevor Gamblin Link: https://lore.kernel.org/r/20240605203507.1934434-3-tgamblin@baylibre.com Signed-off-by: Uwe Kleine-König --- MAINTAINERS | 1 + drivers/pwm/Kconfig | 13 +++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-axi-pwmgen.c | 241 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 256 insertions(+) create mode 100644 drivers/pwm/pwm-axi-pwmgen.c diff --git a/MAINTAINERS b/MAINTAINERS index 09c61e0b614e..ebe38fd0b5b7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3578,6 +3578,7 @@ L: linux-pwm@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml +F: drivers/pwm/pwm-axi-pwmgen.c AXXIA I2C CONTROLLER M: Krzysztof Adamski diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 1dd7921194f5..00a543de8f82 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -94,6 +94,19 @@ config PWM_ATMEL_TCB To compile this driver as a module, choose M here: the module will be called pwm-atmel-tcb. +config PWM_AXI_PWMGEN + tristate "Analog Devices AXI PWM generator" + depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST + select REGMAP_MMIO + help + This enables support for the Analog Devices AXI PWM generator. + + This is a configurable PWM generator with variable pulse width and + period. + + To compile this driver as a module, choose M here: the module will be + called pwm-axi-pwmgen. + config PWM_BCM_IPROC tristate "iProc PWM support" depends on ARCH_BCM_IPROC || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 90913519f11a..6964ba45c795 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PWM_APPLE) += pwm-apple.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o +obj-$(CONFIG_PWM_AXI_PWMGEN) += pwm-axi-pwmgen.o obj-$(CONFIG_PWM_BCM_IPROC) += pwm-bcm-iproc.o obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o diff --git a/drivers/pwm/pwm-axi-pwmgen.c b/drivers/pwm/pwm-axi-pwmgen.c new file mode 100644 index 000000000000..1bd4150e0158 --- /dev/null +++ b/drivers/pwm/pwm-axi-pwmgen.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AXI PWM generator + * + * Copyright 2024 Analog Devices Inc. + * Copyright 2024 Baylibre SAS + * + * Device docs: https://analogdevicesinc.github.io/hdl/library/axi_pwm_gen/index.html + * + * Limitations: + * - The writes to registers for period and duty are shadowed until + * LOAD_CONFIG is written to AXI_PWMGEN_REG_CONFIG, at which point + * they take effect. + * - Writing LOAD_CONFIG also has the effect of re-synchronizing all + * enabled channels, which could cause glitching on other channels. It + * is therefore expected that channels are assigned harmonic periods + * and all have a single user coordinating this. + * - Supports normal polarity. Does not support changing polarity. + * - On disable, the PWM output becomes low (inactive). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXI_PWMGEN_REG_CORE_VERSION 0x00 +#define AXI_PWMGEN_REG_ID 0x04 +#define AXI_PWMGEN_REG_SCRATCHPAD 0x08 +#define AXI_PWMGEN_REG_CORE_MAGIC 0x0C +#define AXI_PWMGEN_REG_CONFIG 0x10 +#define AXI_PWMGEN_REG_NPWM 0x14 +#define AXI_PWMGEN_CHX_PERIOD(ch) (0x40 + (4 * (ch))) +#define AXI_PWMGEN_CHX_DUTY(ch) (0x80 + (4 * (ch))) +#define AXI_PWMGEN_CHX_OFFSET(ch) (0xC0 + (4 * (ch))) +#define AXI_PWMGEN_REG_CORE_MAGIC_VAL 0x601A3471 /* Identification number to test during setup */ +#define AXI_PWMGEN_LOAD_CONFIG BIT(1) +#define AXI_PWMGEN_REG_CONFIG_RESET BIT(0) + +struct axi_pwmgen_ddata { + struct regmap *regmap; + unsigned long clk_rate_hz; +}; + +static const struct regmap_config axi_pwmgen_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +}; + +static int axi_pwmgen_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip); + unsigned int ch = pwm->hwpwm; + struct regmap *regmap = ddata->regmap; + u64 period_cnt, duty_cnt; + int ret; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + if (state->enabled) { + period_cnt = mul_u64_u64_div_u64(state->period, ddata->clk_rate_hz, NSEC_PER_SEC); + if (period_cnt > UINT_MAX) + period_cnt = UINT_MAX; + + if (period_cnt == 0) + return -EINVAL; + + ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), period_cnt); + if (ret) + return ret; + + duty_cnt = mul_u64_u64_div_u64(state->duty_cycle, ddata->clk_rate_hz, NSEC_PER_SEC); + if (duty_cnt > UINT_MAX) + duty_cnt = UINT_MAX; + + ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), duty_cnt); + if (ret) + return ret; + } else { + ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), 0); + if (ret) + return ret; + + ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), 0); + if (ret) + return ret; + } + + return regmap_write(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_LOAD_CONFIG); +} + +static int axi_pwmgen_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip); + struct regmap *regmap = ddata->regmap; + unsigned int ch = pwm->hwpwm; + u32 cnt; + int ret; + + ret = regmap_read(regmap, AXI_PWMGEN_CHX_PERIOD(ch), &cnt); + if (ret) + return ret; + + state->enabled = cnt != 0; + + state->period = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz); + + ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY(ch), &cnt); + if (ret) + return ret; + + state->duty_cycle = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz); + + state->polarity = PWM_POLARITY_NORMAL; + + return 0; +} + +static const struct pwm_ops axi_pwmgen_pwm_ops = { + .apply = axi_pwmgen_apply, + .get_state = axi_pwmgen_get_state, +}; + +static int axi_pwmgen_setup(struct regmap *regmap, struct device *dev) +{ + int ret; + u32 val; + + ret = regmap_read(regmap, AXI_PWMGEN_REG_CORE_MAGIC, &val); + if (ret) + return ret; + + if (val != AXI_PWMGEN_REG_CORE_MAGIC_VAL) + return dev_err_probe(dev, -ENODEV, + "failed to read expected value from register: got %08x, expected %08x\n", + val, AXI_PWMGEN_REG_CORE_MAGIC_VAL); + + ret = regmap_read(regmap, AXI_PWMGEN_REG_CORE_VERSION, &val); + if (ret) + return ret; + + if (ADI_AXI_PCORE_VER_MAJOR(val) != 2) { + return dev_err_probe(dev, -ENODEV, "Unsupported peripheral version %u.%u.%u\n", + ADI_AXI_PCORE_VER_MAJOR(val), + ADI_AXI_PCORE_VER_MINOR(val), + ADI_AXI_PCORE_VER_PATCH(val)); + } + + /* Enable the core */ + ret = regmap_update_bits(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_REG_CONFIG_RESET, 0); + if (ret) + return ret; + + ret = regmap_read(regmap, AXI_PWMGEN_REG_NPWM, &val); + if (ret) + return ret; + + /* Return the number of PWMs */ + return val; +} + +static int axi_pwmgen_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *regmap; + struct pwm_chip *chip; + struct axi_pwmgen_ddata *ddata; + struct clk *clk; + void __iomem *io_base; + int ret; + + io_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + regmap = devm_regmap_init_mmio(dev, io_base, &axi_pwmgen_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "failed to init register map\n"); + + ret = axi_pwmgen_setup(regmap, dev); + if (ret < 0) + return ret; + + chip = devm_pwmchip_alloc(dev, ret, sizeof(*ddata)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + ddata = pwmchip_get_drvdata(chip); + ddata->regmap = regmap; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n"); + + ret = devm_clk_rate_exclusive_get(dev, clk); + if (ret) + return dev_err_probe(dev, ret, "failed to get exclusive rate\n"); + + ddata->clk_rate_hz = clk_get_rate(clk); + if (!ddata->clk_rate_hz || ddata->clk_rate_hz > NSEC_PER_SEC) + return dev_err_probe(dev, -EINVAL, + "Invalid clock rate: %lu\n", ddata->clk_rate_hz); + + chip->ops = &axi_pwmgen_pwm_ops; + chip->atomic = true; + + ret = devm_pwmchip_add(dev, chip); + if (ret) + return dev_err_probe(dev, ret, "could not add PWM chip\n"); + + return 0; +} + +static const struct of_device_id axi_pwmgen_ids[] = { + { .compatible = "adi,axi-pwmgen-2.00.a" }, + { } +}; +MODULE_DEVICE_TABLE(of, axi_pwmgen_ids); + +static struct platform_driver axi_pwmgen_driver = { + .driver = { + .name = "axi-pwmgen", + .of_match_table = axi_pwmgen_ids, + }, + .probe = axi_pwmgen_probe, +}; +module_platform_driver(axi_pwmgen_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sergiu Cuciurean "); +MODULE_AUTHOR("Trevor Gamblin "); +MODULE_DESCRIPTION("Driver for the Analog Devices AXI PWM generator"); -- cgit v1.2.3-58-ga151 From 32b4f1a4f07f1a74687d8de9d6d66038d4646525 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 6 Jun 2024 18:40:46 +0200 Subject: pwm: jz4740: Another few conversions to regmap_{set,clear}_bits() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to commit 7d9199995412 ("pwm: jz4740: Use regmap_{set,clear}_bits") convert two more regmap_update_bits() calls to regmap_{set,clear}_bits() which were missed back then. Signed-off-by: Uwe Kleine-König Reviewed-by: Paul Cercueil Link: https://lore.kernel.org/r/20240606164047.534741-5-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-jz4740.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index da4bf543d357..6bdb01619380 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -201,12 +201,11 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, * state instead of its inactive state. */ if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled) - regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_INITL_HIGH, 0); + regmap_clear_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_INITL_HIGH); else - regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_INITL_HIGH, - TCU_TCSR_PWM_INITL_HIGH); + regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_INITL_HIGH); if (state->enabled) jz4740_pwm_enable(chip, pwm); -- cgit v1.2.3-58-ga151 From 3555f8ff30fe7d19cc2408e7d3bc18720b208e5f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 6 Jun 2024 18:40:47 +0200 Subject: pwm: axi-pwmgen: Make use of regmap_clear_bits() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using regmap_update_bits() and passing val=0, better use regmap_clear_bits(). Signed-off-by: Uwe Kleine-König Acked-by: Trevor Gamblin Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20240606164047.534741-6-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-axi-pwmgen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-axi-pwmgen.c b/drivers/pwm/pwm-axi-pwmgen.c index 1bd4150e0158..aac4f395497b 100644 --- a/drivers/pwm/pwm-axi-pwmgen.c +++ b/drivers/pwm/pwm-axi-pwmgen.c @@ -156,7 +156,7 @@ static int axi_pwmgen_setup(struct regmap *regmap, struct device *dev) } /* Enable the core */ - ret = regmap_update_bits(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_REG_CONFIG_RESET, 0); + ret = regmap_clear_bits(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_REG_CONFIG_RESET); if (ret) return ret; -- cgit v1.2.3-58-ga151 From 2c69747c10d1203073aa4f74d1d09db1b4a89252 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 10 Jun 2024 07:51:15 -0700 Subject: pwm: add missing MODULE_DESCRIPTION() macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pwm/pwm-imx1.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pwm/pwm-imx27.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pwm/pwm-intel-lgm.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pwm/pwm-mediatek.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pwm/pwm-pxa.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pwm/pwm-samsung.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pwm/pwm-spear.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/pwm/pwm-visconti.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Jeff Johnson Link: https://lore.kernel.org/r/20240610-md-drivers-pwm-v2-1-b337cfaa70ea@quicinc.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-imx1.c | 1 + drivers/pwm/pwm-imx27.c | 1 + drivers/pwm/pwm-intel-lgm.c | 1 + drivers/pwm/pwm-mediatek.c | 1 + drivers/pwm/pwm-pxa.c | 1 + drivers/pwm/pwm-samsung.c | 1 + drivers/pwm/pwm-spear.c | 1 + drivers/pwm/pwm-visconti.c | 1 + 8 files changed, 8 insertions(+) diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c index 1d2aae2d278f..d5535d208005 100644 --- a/drivers/pwm/pwm-imx1.c +++ b/drivers/pwm/pwm-imx1.c @@ -194,5 +194,6 @@ static struct platform_driver pwm_imx1_driver = { }; module_platform_driver(pwm_imx1_driver); +MODULE_DESCRIPTION("i.MX1 and i.MX21 Pulse Width Modulator driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Sascha Hauer "); diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index e1412116ef65..9e2bbf5b4a8c 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -352,5 +352,6 @@ static struct platform_driver imx_pwm_driver = { }; module_platform_driver(imx_pwm_driver); +MODULE_DESCRIPTION("i.MX27 and later i.MX SoCs Pulse Width Modulator driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Sascha Hauer "); diff --git a/drivers/pwm/pwm-intel-lgm.c b/drivers/pwm/pwm-intel-lgm.c index f9cc7c17c8f0..084c71a0a11b 100644 --- a/drivers/pwm/pwm-intel-lgm.c +++ b/drivers/pwm/pwm-intel-lgm.c @@ -230,4 +230,5 @@ static struct platform_driver lgm_pwm_driver = { }; module_platform_driver(lgm_pwm_driver); +MODULE_DESCRIPTION("Intel LGM Pulse Width Modulator driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 19a87873ad60..01dfa0fab80a 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -395,4 +395,5 @@ static struct platform_driver pwm_mediatek_driver = { module_platform_driver(pwm_mediatek_driver); MODULE_AUTHOR("John Crispin "); +MODULE_DESCRIPTION("MediaTek general purpose Pulse Width Modulator driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index bb7bb48b2e6d..430bd6a709e9 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -208,4 +208,5 @@ static struct platform_driver pwm_driver = { module_platform_driver(pwm_driver); +MODULE_DESCRIPTION("PXA Pulse Width Modulator driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index efb60c9f0cb3..7adf4f2b1049 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -644,6 +644,7 @@ static struct platform_driver pwm_samsung_driver = { }; module_platform_driver(pwm_samsung_driver); +MODULE_DESCRIPTION("Samsung Pulse Width Modulator driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tomasz Figa "); MODULE_ALIAS("platform:samsung-pwm"); diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c index 6c6f3b38c835..4f372279f313 100644 --- a/drivers/pwm/pwm-spear.c +++ b/drivers/pwm/pwm-spear.c @@ -255,6 +255,7 @@ static struct platform_driver spear_pwm_driver = { module_platform_driver(spear_pwm_driver); +MODULE_DESCRIPTION("ST Microelectronics SPEAr Pulse Width Modulator driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Shiraz Hashim "); MODULE_AUTHOR("Viresh Kumar "); diff --git a/drivers/pwm/pwm-visconti.c b/drivers/pwm/pwm-visconti.c index 9e55380957be..28fae4979e3f 100644 --- a/drivers/pwm/pwm-visconti.c +++ b/drivers/pwm/pwm-visconti.c @@ -170,6 +170,7 @@ static struct platform_driver visconti_pwm_driver = { }; module_platform_driver(visconti_pwm_driver); +MODULE_DESCRIPTION("Toshiba Visconti Pulse Width Modulator driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Nobuhiro Iwamatsu "); MODULE_ALIAS("platform:pwm-visconti"); -- cgit v1.2.3-58-ga151 From 33c651a3fba9a3d380cb0e35ea207e048d39bed9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Jun 2024 18:00:13 +0200 Subject: pwm: Make use of a symbol namespace for the core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define all pwm core's symbols in the namespace "PWM". The necessary module import statement is just added to the main header, this way every file that knows about the public functions automatically has this namespace available. Thanks to Biju Das for pointing out a cut'n'paste failure in my initial patch. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20240607160012.1206874-2-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 2 ++ include/linux/pwm.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 18574857641e..604fd9bc8f22 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -6,6 +6,8 @@ * Copyright (C) 2011-2012 Avionic Design GmbH */ +#define DEFAULT_SYMBOL_NAMESPACE PWM + #include #include #include diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 60b92c2c75ef..812c550de60c 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -4,9 +4,12 @@ #include #include +#include #include #include +MODULE_IMPORT_NS(PWM); + struct pwm_chip; /** -- cgit v1.2.3-58-ga151 From f7d5463e2a8f1fef855af2b62a1f7b59b830ad05 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Jun 2024 10:44:15 +0200 Subject: pwm: cros-ec: Don't care about consumers in .get_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The get_state() callback is never called (in a visible way) after there is a consumer for a pwm device. The core handles loosing the information about duty_cycle just fine. Simplify the driver accordingly. Signed-off-by: Uwe Kleine-König Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20240607084416.897777-6-u.kleine-koenig@baylibre.com [Drop kdoc comment for channel to make W=1 builds happy] Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-cros-ec.c | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c index 606ccfdaf4cc..ea0dd553a626 100644 --- a/drivers/pwm/pwm-cros-ec.c +++ b/drivers/pwm/pwm-cros-ec.c @@ -20,20 +20,10 @@ * * @ec: Pointer to EC device * @use_pwm_type: Use PWM types instead of generic channels - * @channel: array with per-channel data */ struct cros_ec_pwm_device { struct cros_ec_device *ec; bool use_pwm_type; - struct cros_ec_pwm *channel; -}; - -/** - * struct cros_ec_pwm - per-PWM driver data - * @duty_cycle: cached duty cycle - */ -struct cros_ec_pwm { - u16 duty_cycle; }; static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *chip) @@ -135,7 +125,6 @@ static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip); - struct cros_ec_pwm *channel = &ec_pwm->channel[pwm->hwpwm]; u16 duty_cycle; int ret; @@ -156,8 +145,6 @@ static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (ret < 0) return ret; - channel->duty_cycle = state->duty_cycle; - return 0; } @@ -165,7 +152,6 @@ static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip); - struct cros_ec_pwm *channel = &ec_pwm->channel[pwm->hwpwm]; int ret; ret = cros_ec_pwm_get_duty(ec_pwm->ec, ec_pwm->use_pwm_type, pwm->hwpwm); @@ -175,23 +161,10 @@ static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, } state->enabled = (ret > 0); + state->duty_cycle = ret; state->period = EC_PWM_MAX_DUTY; state->polarity = PWM_POLARITY_NORMAL; - /* - * Note that "disabled" and "duty cycle == 0" are treated the same. If - * the cached duty cycle is not zero, used the cached duty cycle. This - * ensures that the configured duty cycle is kept across a disable and - * enable operation and avoids potentially confusing consumers. - * - * For the case of the initial hardware readout, channel->duty_cycle - * will be 0 and the actual duty cycle read from the EC is used. - */ - if (ret == 0 && channel->duty_cycle > 0) - state->duty_cycle = channel->duty_cycle; - else - state->duty_cycle = ret; - return 0; } @@ -291,11 +264,6 @@ static int cros_ec_pwm_probe(struct platform_device *pdev) chip->ops = &cros_ec_pwm_ops; chip->of_xlate = cros_ec_pwm_xlate; - ec_pwm->channel = devm_kcalloc(dev, chip->npwm, sizeof(*ec_pwm->channel), - GFP_KERNEL); - if (!ec_pwm->channel) - return -ENOMEM; - dev_dbg(dev, "Probed %u PWMs\n", chip->npwm); ret = devm_pwmchip_add(dev, chip); -- cgit v1.2.3-58-ga151 From 40571a5b3b73c6c5189e55ff82b97a58bec371a1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Jun 2024 10:44:16 +0200 Subject: pwm: cros-ec: Simplify device tree xlation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cros-ec device tree binding only uses #pwm-cells = <1>, and so there is no period provided in the device tree. Up to now this was handled by hardcoding the period to the only supported value in the custom xlate callback. Apart from that, the default xlate callback (i.e. of_pwm_xlate_with_flags()) handles this just fine (and better, e.g. by checking args->args_count >= 1 before accessing args->args[0]). To simplify make use of of_pwm_xlate_with_flags(), drop the custom callback and provide the default period in .probe() already. Apart from simplifying the driver this also drops the last non-core user of pwm_request_from_chip() and so makes further simplifications possible. Signed-off-by: Uwe Kleine-König Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20240607084416.897777-7-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-cros-ec.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c index ea0dd553a626..189301dc395e 100644 --- a/drivers/pwm/pwm-cros-ec.c +++ b/drivers/pwm/pwm-cros-ec.c @@ -168,24 +168,6 @@ static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } -static struct pwm_device * -cros_ec_pwm_xlate(struct pwm_chip *chip, const struct of_phandle_args *args) -{ - struct pwm_device *pwm; - - if (args->args[0] >= chip->npwm) - return ERR_PTR(-EINVAL); - - pwm = pwm_request_from_chip(chip, args->args[0], NULL); - if (IS_ERR(pwm)) - return pwm; - - /* The EC won't let us change the period */ - pwm->args.period = EC_PWM_MAX_DUTY; - - return pwm; -} - static const struct pwm_ops cros_ec_pwm_ops = { .get_state = cros_ec_pwm_get_state, .apply = cros_ec_pwm_apply, @@ -236,7 +218,7 @@ static int cros_ec_pwm_probe(struct platform_device *pdev) struct cros_ec_pwm_device *ec_pwm; struct pwm_chip *chip; bool use_pwm_type = false; - unsigned int npwm; + unsigned int i, npwm; int ret; if (!ec) @@ -262,7 +244,17 @@ static int cros_ec_pwm_probe(struct platform_device *pdev) /* PWM chip */ chip->ops = &cros_ec_pwm_ops; - chip->of_xlate = cros_ec_pwm_xlate; + + /* + * The device tree binding for this device is special as it only uses a + * single cell (for the hwid) and so doesn't provide a default period. + * This isn't a big problem though as the hardware only supports a + * single period length, it's just a bit ugly to make this fit into the + * pwm core abstractions. So initialize the period here, as + * of_pwm_xlate_with_flags() won't do that for us. + */ + for (i = 0; i < npwm; ++i) + chip->pwms[i].args.period = EC_PWM_MAX_DUTY; dev_dbg(dev, "Probed %u PWMs\n", chip->npwm); -- cgit v1.2.3-58-ga151 From d6f66e292676db976c4d42df2631427236c36fdb Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Jun 2024 10:44:17 +0200 Subject: pwm: Make pwm_request_from_chip() private to the core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last user of this function outside of core.c is gone, so it can be made static. Signed-off-by: Uwe Kleine-König Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20240607084416.897777-8-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 8 +++----- include/linux/pwm.h | 12 ------------ 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 604fd9bc8f22..5c1d20985148 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -396,9 +396,9 @@ err_get_device: * chip. A negative error code is returned if the index is not valid for the * specified PWM chip or if the PWM device cannot be requested. */ -struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, - unsigned int index, - const char *label) +static struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, + unsigned int index, + const char *label) { struct pwm_device *pwm; int err; @@ -416,8 +416,6 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, mutex_unlock(&pwm_lock); return pwm; } -EXPORT_SYMBOL_GPL(pwm_request_from_chip); - struct pwm_device * of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args) diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 812c550de60c..2e225f3b4282 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -410,10 +410,6 @@ void pwmchip_remove(struct pwm_chip *chip); int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner); #define devm_pwmchip_add(dev, chip) __devm_pwmchip_add(dev, chip, THIS_MODULE) -struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, - unsigned int index, - const char *label); - struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args); struct pwm_device *of_pwm_single_xlate(struct pwm_chip *chip, @@ -508,14 +504,6 @@ static inline int devm_pwmchip_add(struct device *dev, struct pwm_chip *chip) return -EINVAL; } -static inline struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, - unsigned int index, - const char *label) -{ - might_sleep(); - return ERR_PTR(-ENODEV); -} - static inline struct pwm_device *pwm_get(struct device *dev, const char *consumer) { -- cgit v1.2.3-58-ga151 From a96d3659c9437673dbef5c05cba31a3c0b5a0a7b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Jun 2024 12:42:31 +0200 Subject: pwm: Remove wrong implementation details from pwm_ops's documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When .get_state() is called is an implementation detail that implementors and users shouldn't care about and rely on. Additionally it's wrong, because with PWM_DEBUG enabled it is called more often. Just drop the wrong statement. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/611ba758d7e9fb2695e96b23cb7ceeefb6ba8513.1717756902.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- include/linux/pwm.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 2e225f3b4282..75ad0d2fd949 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -252,9 +252,7 @@ struct pwm_capture { * @free: optional hook for freeing a PWM * @capture: capture and report PWM signal * @apply: atomically apply a new PWM config - * @get_state: get the current PWM state. This function is only - * called once per PWM device when the PWM chip is - * registered. + * @get_state: get the current PWM state. */ struct pwm_ops { int (*request)(struct pwm_chip *chip, struct pwm_device *pwm); -- cgit v1.2.3-58-ga151 From f8b03e5c728ff6cf76f9db23dbfaf75b7eeb0a12 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Fri, 14 Jun 2024 10:08:29 +0100 Subject: bus: ts-nbus: Use pwm_apply_might_sleep() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pwm_apply_state() is deprecated since commit c748a6d77c06a ("pwm: Rename pwm_apply_state() to pwm_apply_might_sleep()"). Signed-off-by: Sean Young Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20240614090829.560605-1-sean@mess.org Signed-off-by: Uwe Kleine-König --- drivers/bus/ts-nbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c index baf22a82c47a..b8af44c5cdbd 100644 --- a/drivers/bus/ts-nbus.c +++ b/drivers/bus/ts-nbus.c @@ -294,7 +294,7 @@ static int ts_nbus_probe(struct platform_device *pdev) state.duty_cycle = state.period; state.enabled = true; - ret = pwm_apply_state(pwm, &state); + ret = pwm_apply_might_sleep(pwm, &state); if (ret < 0) return dev_err_probe(dev, ret, "failed to configure PWM\n"); -- cgit v1.2.3-58-ga151 From da804fa9bc718e4210ec27cc0457ecc1eb073a14 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 14 Jun 2024 17:39:00 +0200 Subject: pwm: Drop pwm_apply_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is not supposed to be used any more since commit c748a6d77c06 ("pwm: Rename pwm_apply_state() to pwm_apply_might_sleep()") that is included in v6.8-rc1. Two kernel releases should be enough for everyone to adapt, so drop the old function that was introduced as a compatibility stub for the transition. Signed-off-by: Uwe Kleine-König --- include/linux/pwm.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 75ad0d2fd949..f8c2dc12dbd3 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -563,13 +563,6 @@ static inline void pwm_apply_args(struct pwm_device *pwm) pwm_apply_might_sleep(pwm, &state); } -/* only for backwards-compatibility, new code should not use this */ -static inline int pwm_apply_state(struct pwm_device *pwm, - const struct pwm_state *state) -{ - return pwm_apply_might_sleep(pwm, state); -} - struct pwm_lookup { struct list_head list; const char *provider; -- cgit v1.2.3-58-ga151 From 1577ddaa515e3af2614e1f52711c287bebc338cf Mon Sep 17 00:00:00 2001 From: Nicola Di Lieto Date: Tue, 4 Jun 2024 23:00:40 +0200 Subject: dt-bindings: pwm: Add pwm-gpio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add bindings for PWM modulated by GPIO. Signed-off-by: Nicola Di Lieto Co-developed-by: Stefan Wahren Signed-off-by: Stefan Wahren Reviewed-by: Linus Walleij Reviewed-by: Krzysztof Kozlowski Reviewed-by: Dhruva Gole Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240604-pwm-gpio-v7-1-6b67cf60db92@linaro.org Signed-off-by: Uwe Kleine-König --- .../devicetree/bindings/pwm/pwm-gpio.yaml | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-gpio.yaml diff --git a/Documentation/devicetree/bindings/pwm/pwm-gpio.yaml b/Documentation/devicetree/bindings/pwm/pwm-gpio.yaml new file mode 100644 index 000000000000..1576c193f2ab --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-gpio.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/pwm-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic software PWM for modulating GPIOs + +maintainers: + - Stefan Wahren + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: pwm-gpio + + "#pwm-cells": + const: 3 + description: + See pwm.yaml in this directory for a description of the cells format. + The first cell which represents the PWM instance number must always + be zero. + + gpios: + description: + GPIO to be modulated + maxItems: 1 + +required: + - compatible + - "#pwm-cells" + - gpios + +additionalProperties: false + +examples: + - | + #include + + pwm { + #pwm-cells = <3>; + compatible = "pwm-gpio"; + gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; + }; -- cgit v1.2.3-58-ga151 From 7f61257cd6e1ad4769b4b819668cab00f68f2556 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Tue, 4 Jun 2024 23:00:41 +0200 Subject: pwm: Add GPIO PWM driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a software PWM which toggles a GPIO from a high-resolution timer. This will naturally not be as accurate or as efficient as a hardware PWM, but it is useful in some cases. I have for example used it for evaluating LED brightness handling (via leds-pwm) on a board where the LED was just hooked up to a GPIO, and for a simple verification of the timer frequency on another platform. Since high-resolution timers are used, sleeping GPIO chips are not supported and are rejected in the probe function. Signed-off-by: Vincent Whitchurch Co-developed-by: Stefan Wahren Signed-off-by: Stefan Wahren Co-developed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij Reviewed-by: Dhruva Gole Link: https://lore.kernel.org/r/20240604-pwm-gpio-v7-2-6b67cf60db92@linaro.org Signed-off-by: Uwe Kleine-König --- Documentation/driver-api/gpio/drivers-on-gpio.rst | 7 +- drivers/pwm/Kconfig | 11 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-gpio.c | 241 ++++++++++++++++++++++ 4 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 drivers/pwm/pwm-gpio.c diff --git a/Documentation/driver-api/gpio/drivers-on-gpio.rst b/Documentation/driver-api/gpio/drivers-on-gpio.rst index af632d764ac6..95572d2a94ce 100644 --- a/Documentation/driver-api/gpio/drivers-on-gpio.rst +++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst @@ -27,7 +27,12 @@ hardware descriptions such as device tree or ACPI: to the lines for a more permanent solution of this type. - gpio-beeper: drivers/input/misc/gpio-beeper.c is used to provide a beep from - an external speaker connected to a GPIO line. + an external speaker connected to a GPIO line. (If the beep is controlled by + off/on, for an actual PWM waveform, see pwm-gpio below.) + +- pwm-gpio: drivers/pwm/pwm-gpio.c is used to toggle a GPIO with a high + resolution timer producing a PWM waveform on the GPIO line, as well as + Linux high resolution timers can do. - extcon-gpio: drivers/extcon/extcon-gpio.c is used when you need to read an external connector status, such as a headset line for an audio driver or an diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 00a543de8f82..3e53838990f5 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -236,6 +236,17 @@ config PWM_FSL_FTM To compile this driver as a module, choose M here: the module will be called pwm-fsl-ftm. +config PWM_GPIO + tristate "GPIO PWM support" + depends on GPIOLIB + depends on HIGH_RES_TIMERS + help + Generic PWM framework driver for software PWM toggling a GPIO pin + from kernel high-resolution timers. + + To compile this driver as a module, choose M here: the module + will be called pwm-gpio. + config PWM_HIBVT tristate "HiSilicon BVT PWM support" depends on ARCH_HISI || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 6964ba45c795..0be4f3e6dd43 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_PWM_DWC_CORE) += pwm-dwc-core.o obj-$(CONFIG_PWM_DWC) += pwm-dwc.o obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o +obj-$(CONFIG_PWM_GPIO) += pwm-gpio.o obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o obj-$(CONFIG_PWM_IMG) += pwm-img.o obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o diff --git a/drivers/pwm/pwm-gpio.c b/drivers/pwm/pwm-gpio.c new file mode 100644 index 000000000000..9f8884ac7504 --- /dev/null +++ b/drivers/pwm/pwm-gpio.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Generic software PWM for modulating GPIOs + * + * Copyright (C) 2020 Axis Communications AB + * Copyright (C) 2020 Nicola Di Lieto + * Copyright (C) 2024 Stefan Wahren + * Copyright (C) 2024 Linus Walleij + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pwm_gpio { + struct hrtimer gpio_timer; + struct gpio_desc *gpio; + struct pwm_state state; + struct pwm_state next_state; + + /* Protect internal state between pwm_ops and hrtimer */ + spinlock_t lock; + + bool changing; + bool running; + bool level; +}; + +static void pwm_gpio_round(struct pwm_state *dest, const struct pwm_state *src) +{ + u64 dividend; + u32 remainder; + + *dest = *src; + + /* Round down to hrtimer resolution */ + dividend = dest->period; + remainder = do_div(dividend, hrtimer_resolution); + dest->period -= remainder; + + dividend = dest->duty_cycle; + remainder = do_div(dividend, hrtimer_resolution); + dest->duty_cycle -= remainder; +} + +static u64 pwm_gpio_toggle(struct pwm_gpio *gpwm, bool level) +{ + const struct pwm_state *state = &gpwm->state; + bool invert = state->polarity == PWM_POLARITY_INVERSED; + + gpwm->level = level; + gpiod_set_value(gpwm->gpio, gpwm->level ^ invert); + + if (!state->duty_cycle || state->duty_cycle == state->period) { + gpwm->running = false; + return 0; + } + + gpwm->running = true; + return level ? state->duty_cycle : state->period - state->duty_cycle; +} + +static enum hrtimer_restart pwm_gpio_timer(struct hrtimer *gpio_timer) +{ + struct pwm_gpio *gpwm = container_of(gpio_timer, struct pwm_gpio, + gpio_timer); + u64 next_toggle; + bool new_level; + + guard(spinlock_irqsave)(&gpwm->lock); + + /* Apply new state at end of current period */ + if (!gpwm->level && gpwm->changing) { + gpwm->changing = false; + gpwm->state = gpwm->next_state; + new_level = !!gpwm->state.duty_cycle; + } else { + new_level = !gpwm->level; + } + + next_toggle = pwm_gpio_toggle(gpwm, new_level); + if (next_toggle) + hrtimer_forward(gpio_timer, hrtimer_get_expires(gpio_timer), + ns_to_ktime(next_toggle)); + + return next_toggle ? HRTIMER_RESTART : HRTIMER_NORESTART; +} + +static int pwm_gpio_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct pwm_gpio *gpwm = pwmchip_get_drvdata(chip); + bool invert = state->polarity == PWM_POLARITY_INVERSED; + + if (state->duty_cycle && state->duty_cycle < hrtimer_resolution) + return -EINVAL; + + if (state->duty_cycle != state->period && + (state->period - state->duty_cycle < hrtimer_resolution)) + return -EINVAL; + + if (!state->enabled) { + hrtimer_cancel(&gpwm->gpio_timer); + } else if (!gpwm->running) { + int ret; + + /* + * This just enables the output, but pwm_gpio_toggle() + * really starts the duty cycle. + */ + ret = gpiod_direction_output(gpwm->gpio, invert); + if (ret) + return ret; + } + + guard(spinlock_irqsave)(&gpwm->lock); + + if (!state->enabled) { + pwm_gpio_round(&gpwm->state, state); + gpwm->running = false; + gpwm->changing = false; + + gpiod_set_value(gpwm->gpio, invert); + } else if (gpwm->running) { + pwm_gpio_round(&gpwm->next_state, state); + gpwm->changing = true; + } else { + unsigned long next_toggle; + + pwm_gpio_round(&gpwm->state, state); + gpwm->changing = false; + + next_toggle = pwm_gpio_toggle(gpwm, !!state->duty_cycle); + if (next_toggle) + hrtimer_start(&gpwm->gpio_timer, next_toggle, + HRTIMER_MODE_REL); + } + + return 0; +} + +static int pwm_gpio_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct pwm_gpio *gpwm = pwmchip_get_drvdata(chip); + + guard(spinlock_irqsave)(&gpwm->lock); + + if (gpwm->changing) + *state = gpwm->next_state; + else + *state = gpwm->state; + + return 0; +} + +static const struct pwm_ops pwm_gpio_ops = { + .apply = pwm_gpio_apply, + .get_state = pwm_gpio_get_state, +}; + +static void pwm_gpio_disable_hrtimer(void *data) +{ + struct pwm_gpio *gpwm = data; + + hrtimer_cancel(&gpwm->gpio_timer); +} + +static int pwm_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pwm_chip *chip; + struct pwm_gpio *gpwm; + int ret; + + chip = devm_pwmchip_alloc(dev, 1, sizeof(*gpwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + gpwm = pwmchip_get_drvdata(chip); + + spin_lock_init(&gpwm->lock); + + gpwm->gpio = devm_gpiod_get(dev, NULL, GPIOD_ASIS); + if (IS_ERR(gpwm->gpio)) + return dev_err_probe(dev, PTR_ERR(gpwm->gpio), + "%pfw: could not get gpio\n", + dev_fwnode(dev)); + + if (gpiod_cansleep(gpwm->gpio)) + return dev_err_probe(dev, -EINVAL, + "%pfw: sleeping GPIO not supported\n", + dev_fwnode(dev)); + + chip->ops = &pwm_gpio_ops; + chip->atomic = true; + + hrtimer_init(&gpwm->gpio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ret = devm_add_action_or_reset(dev, pwm_gpio_disable_hrtimer, gpwm); + if (ret) + return ret; + + gpwm->gpio_timer.function = pwm_gpio_timer; + + ret = pwmchip_add(chip); + if (ret < 0) + return dev_err_probe(dev, ret, "could not add pwmchip\n"); + + return 0; +} + +static const struct of_device_id pwm_gpio_dt_ids[] = { + { .compatible = "pwm-gpio" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pwm_gpio_dt_ids); + +static struct platform_driver pwm_gpio_driver = { + .driver = { + .name = "pwm-gpio", + .of_match_table = pwm_gpio_dt_ids, + }, + .probe = pwm_gpio_probe, +}; +module_platform_driver(pwm_gpio_driver); + +MODULE_DESCRIPTION("PWM GPIO driver"); +MODULE_AUTHOR("Vincent Whitchurch"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From 2ed3284f3120caf0221276fa6cbc97f8867b354f Mon Sep 17 00:00:00 2001 From: Junyi Zhao Date: Thu, 13 Jun 2024 19:46:35 +0800 Subject: pwm: meson: Add support for Amlogic S4 PWM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Amlogic S4 PWM. Signed-off-by: Junyi Zhao Signed-off-by: Kelvin Zhang Reviewed-by: George Stark Link: https://lore.kernel.org/r/20240613-s4-pwm-v8-1-b5bd0a768282@amlogic.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-meson.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index b2f97dfb01bb..98e6c1533312 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -460,6 +460,37 @@ static int meson_pwm_init_channels_meson8b_v2(struct pwm_chip *chip) return meson_pwm_init_clocks_meson8b(chip, mux_parent_data); } +static void meson_pwm_s4_put_clk(void *data) +{ + struct clk *clk = data; + + clk_put(clk); +} + +static int meson_pwm_init_channels_s4(struct pwm_chip *chip) +{ + struct device *dev = pwmchip_parent(chip); + struct device_node *np = dev->of_node; + struct meson_pwm *meson = to_meson_pwm(chip); + int i, ret; + + for (i = 0; i < MESON_NUM_PWMS; i++) { + meson->channels[i].clk = of_clk_get(np, i); + if (IS_ERR(meson->channels[i].clk)) + return dev_err_probe(dev, + PTR_ERR(meson->channels[i].clk), + "Failed to get clk\n"); + + ret = devm_add_action_or_reset(dev, meson_pwm_s4_put_clk, + meson->channels[i].clk); + if (ret) + return dev_err_probe(dev, ret, + "Failed to add clk_put action\n"); + } + + return 0; +} + static const struct meson_pwm_data pwm_meson8b_data = { .parent_names = { "xtal", NULL, "fclk_div4", "fclk_div3" }, .channels_init = meson_pwm_init_channels_meson8b_legacy, @@ -498,6 +529,10 @@ static const struct meson_pwm_data pwm_meson8_v2_data = { .channels_init = meson_pwm_init_channels_meson8b_v2, }; +static const struct meson_pwm_data pwm_s4_data = { + .channels_init = meson_pwm_init_channels_s4, +}; + static const struct of_device_id meson_pwm_matches[] = { { .compatible = "amlogic,meson8-pwm-v2", @@ -536,6 +571,10 @@ static const struct of_device_id meson_pwm_matches[] = { .compatible = "amlogic,meson-g12a-ao-pwm-cd", .data = &pwm_g12a_ao_cd_data }, + { + .compatible = "amlogic,meson-s4-pwm", + .data = &pwm_s4_data + }, {}, }; MODULE_DEVICE_TABLE(of, meson_pwm_matches); -- cgit v1.2.3-58-ga151 From 30122ce2b9a2890595d452c746bb07a08ac591d1 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 5 Jun 2024 18:08:39 -0400 Subject: dt-bindings: pwm: imx: remove interrupt property from required MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver "drivers/pwm/pwm-imx27.c" never use interrupt. Generally pwm hardware generate a waveform according to register timing setting. Needn't interrupt handle at all. So remove it from "required" list. Signed-off-by: Frank Li Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240605220839.1398872-1-Frank.Li@nxp.com Signed-off-by: Uwe Kleine-König --- Documentation/devicetree/bindings/pwm/imx-pwm.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.yaml b/Documentation/devicetree/bindings/pwm/imx-pwm.yaml index a84a240a61dc..04148198e34d 100644 --- a/Documentation/devicetree/bindings/pwm/imx-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/imx-pwm.yaml @@ -68,7 +68,6 @@ required: - reg - clocks - clock-names - - interrupts additionalProperties: false -- cgit v1.2.3-58-ga151 From 7cea05ae1d4ecbb7a6c3d28f9c483b1f9105526a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 19 Jun 2024 12:11:46 +0200 Subject: pwm-stm32: Make use of parametrised register definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no semantic change, but it is a nicer on the eyes of a reader, because TIM_CCR1 + 4 * ch encodes internal register knowledge, while TIM_CCRx(ch + 1) keeps that information completely in the header defining the registers. While I expected this to not result in any changes in the binary, gcc 13 (as provided by Debian in the gcc-13-arm-linux-gnueabihf 13.2.0-12cross1 package) compiles the new version with an allmodconfig to more compact code: $ source/scripts/bloat-o-meter drivers/pwm/pwm-stm32.o-pre drivers/pwm/pwm-stm32.o add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-488 (-488) Function old new delta stm32_pwm_get_state 968 936 -32 stm32_pwm_apply_locked 1920 1464 -456 Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/d7ef7a6158df4ba6687233b0e00d37796b069fb3.1718791090.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-stm32.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index c586029caf23..fd754a99cf2e 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -368,7 +368,7 @@ static int stm32_pwm_config(struct stm32_pwm *priv, unsigned int ch, dty = mul_u64_u64_div_u64(duty_ns, clk_get_rate(priv->clk), (u64)NSEC_PER_SEC * (prescaler + 1)); - regmap_write(priv->regmap, TIM_CCR1 + 4 * ch, dty); + regmap_write(priv->regmap, TIM_CCRx(ch + 1), dty); /* Configure output mode */ shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT; @@ -390,9 +390,9 @@ static int stm32_pwm_set_polarity(struct stm32_pwm *priv, unsigned int ch, { u32 mask; - mask = TIM_CCER_CC1P << (ch * 4); + mask = TIM_CCER_CCxP(ch + 1); if (priv->have_complementary_output) - mask |= TIM_CCER_CC1NP << (ch * 4); + mask |= TIM_CCER_CCxNP(ch + 1); regmap_update_bits(priv->regmap, TIM_CCER, mask, polarity == PWM_POLARITY_NORMAL ? 0 : mask); @@ -410,9 +410,9 @@ static int stm32_pwm_enable(struct stm32_pwm *priv, unsigned int ch) return ret; /* Enable channel */ - mask = TIM_CCER_CC1E << (ch * 4); + mask = TIM_CCER_CCxE(ch + 1); if (priv->have_complementary_output) - mask |= TIM_CCER_CC1NE << (ch * 4); + mask |= TIM_CCER_CCxNE(ch); regmap_set_bits(priv->regmap, TIM_CCER, mask); @@ -430,9 +430,9 @@ static void stm32_pwm_disable(struct stm32_pwm *priv, unsigned int ch) u32 mask; /* Disable channel */ - mask = TIM_CCER_CC1E << (ch * 4); + mask = TIM_CCER_CCxE(ch + 1); if (priv->have_complementary_output) - mask |= TIM_CCER_CC1NE << (ch * 4); + mask |= TIM_CCER_CCxNE(ch + 1); regmap_clear_bits(priv->regmap, TIM_CCER, mask); @@ -502,8 +502,8 @@ static int stm32_pwm_get_state(struct pwm_chip *chip, if (ret) goto out; - state->enabled = ccer & (TIM_CCER_CC1E << (ch * 4)); - state->polarity = (ccer & (TIM_CCER_CC1P << (ch * 4))) ? + state->enabled = ccer & TIM_CCER_CCxE(ch + 1); + state->polarity = (ccer & TIM_CCER_CCxP(ch + 1)) ? PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL; ret = regmap_read(priv->regmap, TIM_PSC, &psc); if (ret) @@ -511,7 +511,7 @@ static int stm32_pwm_get_state(struct pwm_chip *chip, ret = regmap_read(priv->regmap, TIM_ARR, &arr); if (ret) goto out; - ret = regmap_read(priv->regmap, TIM_CCR1 + 4 * ch, &ccr); + ret = regmap_read(priv->regmap, TIM_CCRx(ch + 1), &ccr); if (ret) goto out; @@ -712,7 +712,7 @@ static int stm32_pwm_suspend(struct device *dev) ccer = active_channels(priv); for (i = 0; i < chip->npwm; i++) { - mask = TIM_CCER_CC1E << (i * 4); + mask = TIM_CCER_CCxE(i + 1); if (ccer & mask) { dev_err(dev, "PWM %u still in use by consumer %s\n", i, chip->pwms[i].label); -- cgit v1.2.3-58-ga151 From 07612a7621ce3fb5c450877b2ecc76ef4d3c0bf9 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Wed, 5 Jun 2024 18:45:32 +0530 Subject: pwm: lpss: use devm_pm_runtime_enable() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use devm_pm_runtime_enable() helper to enable runtime PM and drop redundant platform ->remove() callback. Reviewed-by: Andy Shevchenko Signed-off-by: Raag Jadav Link: https://lore.kernel.org/r/20240605131533.20037-2-raag.jadav@intel.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-lpss-platform.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c index dbc9f5b17bdc..5130238a4567 100644 --- a/drivers/pwm/pwm-lpss-platform.c +++ b/drivers/pwm/pwm-lpss-platform.c @@ -55,14 +55,7 @@ static int pwm_lpss_probe_platform(struct platform_device *pdev) DPM_FLAG_SMART_SUSPEND); pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - - return 0; -} - -static void pwm_lpss_remove_platform(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); + return devm_pm_runtime_enable(&pdev->dev); } static const struct acpi_device_id pwm_lpss_acpi_match[] = { @@ -80,7 +73,6 @@ static struct platform_driver pwm_lpss_driver_platform = { .acpi_match_table = pwm_lpss_acpi_match, }, .probe = pwm_lpss_probe_platform, - .remove_new = pwm_lpss_remove_platform, }; module_platform_driver(pwm_lpss_driver_platform); -- cgit v1.2.3-58-ga151 From f3a616e2a8755bb45f72d93aee3ac1b7e808182d Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Wed, 5 Jun 2024 18:45:33 +0530 Subject: pwm: lpss: drop redundant runtime PM handles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We no longer need empty runtime PM handles for PCI devices after commits [1] and [2]. Drop them and let PCI core take care of power state transitions. [1] c5eb1190074c ("PCI / PM: Allow runtime PM without callback functions") [2] fa885b06ec7e ("PCI/PM: Allow runtime PM with no PM callbacks at all") Reviewed-by: Andy Shevchenko Signed-off-by: Raag Jadav Link: https://lore.kernel.org/r/20240605131533.20037-3-raag.jadav@intel.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-lpss-pci.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c index 25045c229520..f7ece2809e6b 100644 --- a/drivers/pwm/pwm-lpss-pci.c +++ b/drivers/pwm/pwm-lpss-pci.c @@ -46,25 +46,6 @@ static void pwm_lpss_remove_pci(struct pci_dev *pdev) pm_runtime_get_sync(&pdev->dev); } -static int pwm_lpss_runtime_suspend_pci(struct device *dev) -{ - /* - * The PCI core will handle transition to D3 automatically. We only - * need to provide runtime PM hooks for that to happen. - */ - return 0; -} - -static int pwm_lpss_runtime_resume_pci(struct device *dev) -{ - return 0; -} - -static DEFINE_RUNTIME_DEV_PM_OPS(pwm_lpss_pci_pm, - pwm_lpss_runtime_suspend_pci, - pwm_lpss_runtime_resume_pci, - NULL); - static const struct pci_device_id pwm_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x0ac8), (unsigned long)&pwm_lpss_bxt_info}, { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info}, @@ -84,9 +65,6 @@ static struct pci_driver pwm_lpss_driver_pci = { .id_table = pwm_lpss_pci_ids, .probe = pwm_lpss_probe_pci, .remove = pwm_lpss_remove_pci, - .driver = { - .pm = pm_ptr(&pwm_lpss_pci_pm), - }, }; module_pci_driver(pwm_lpss_driver_pci); -- cgit v1.2.3-58-ga151 From 9b22ceb31a7dda9a78959db3c6e35b04888032f9 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Tue, 2 Jul 2024 11:45:14 -0500 Subject: pwm: imx-tpm: Enable pinctrl setting for sleep state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply the pinctrl setting of sleep state when system enters suspend state. Restore to the default pinctrl setting when system resumes. Signed-off-by: Shenwei Wang Link: https://lore.kernel.org/r/20240702164514.11007-1-shenwei.wang@nxp.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-imx-tpm.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c index c50ddbac43c8..96ea343856f0 100644 --- a/drivers/pwm/pwm-imx-tpm.c +++ b/drivers/pwm/pwm-imx-tpm.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -380,6 +381,7 @@ static int pwm_imx_tpm_probe(struct platform_device *pdev) static int pwm_imx_tpm_suspend(struct device *dev) { struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev); + int ret; if (tpm->enable_count > 0) return -EBUSY; @@ -393,7 +395,11 @@ static int pwm_imx_tpm_suspend(struct device *dev) clk_disable_unprepare(tpm->clk); - return 0; + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) + clk_prepare_enable(tpm->clk); + + return ret; } static int pwm_imx_tpm_resume(struct device *dev) @@ -401,9 +407,15 @@ static int pwm_imx_tpm_resume(struct device *dev) struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev); int ret = 0; - ret = clk_prepare_enable(tpm->clk); + ret = pinctrl_pm_select_default_state(dev); if (ret) + return ret; + + ret = clk_prepare_enable(tpm->clk); + if (ret) { dev_err(dev, "failed to prepare or enable clock: %d\n", ret); + pinctrl_pm_select_sleep_state(dev); + } return ret; } -- cgit v1.2.3-58-ga151 From 44ee95184e785c64e301498dec112bb1582bffd2 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 27 Jun 2024 00:25:27 +0200 Subject: pwm: Register debugfs operations after the pwm class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the debugfs operations don't technically depend on an initialized class, they loop over the idr that only can get entries when the class is properly initialized. This also fixes the ugly (but harmless) corner case that the debugfs file stays around after the pwm class failed to initialize. While at it, add an appropriate error message when class initialization fails. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20240626222529.2901200-2-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 5c1d20985148..84ca846120a2 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -1705,9 +1705,17 @@ DEFINE_SEQ_ATTRIBUTE(pwm_debugfs); static int __init pwm_init(void) { + int ret; + + ret = class_register(&pwm_class); + if (ret) { + pr_err("Failed to initialize PWM class (%pe)\n", ERR_PTR(ret)); + return ret; + } + if (IS_ENABLED(CONFIG_DEBUG_FS)) debugfs_create_file("pwm", 0444, NULL, NULL, &pwm_debugfs_fops); - return class_register(&pwm_class); + return 0; } subsys_initcall(pwm_init); -- cgit v1.2.3-58-ga151 From 650af6c0833a7274a755f5ed4bae29e53bbae4fc Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 27 Jun 2024 22:31:19 +0200 Subject: pwm: Use guards for pwm_lock instead of explicity mutex_lock + mutex_unlock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the compiler caring for unlocking the mutex several functions can be simplified. Benefit from that. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/2102fe8189bdf1f02ff3785b551a69be27a65af4.1719520143.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 52 +++++++++++++++------------------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 84ca846120a2..bce42fbbe2c3 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -293,19 +293,15 @@ EXPORT_SYMBOL_GPL(pwm_adjust_config); int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result, unsigned long timeout) { - int err; - if (!pwm || !pwm->chip->ops) return -EINVAL; if (!pwm->chip->ops->capture) return -ENOSYS; - mutex_lock(&pwm_lock); - err = pwm->chip->ops->capture(pwm->chip, pwm, result, timeout); - mutex_unlock(&pwm_lock); + guard(mutex)(&pwm_lock); - return err; + return pwm->chip->ops->capture(pwm->chip, pwm, result, timeout); } EXPORT_SYMBOL_GPL(pwm_capture); @@ -317,19 +313,15 @@ static struct pwm_chip *pwmchip_find_by_name(const char *name) if (!name) return NULL; - mutex_lock(&pwm_lock); + guard(mutex)(&pwm_lock); idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) { const char *chip_name = dev_name(pwmchip_parent(chip)); - if (chip_name && strcmp(chip_name, name) == 0) { - mutex_unlock(&pwm_lock); + if (chip_name && strcmp(chip_name, name) == 0) return chip; - } } - mutex_unlock(&pwm_lock); - return NULL; } @@ -406,14 +398,14 @@ static struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, if (!chip || index >= chip->npwm) return ERR_PTR(-EINVAL); - mutex_lock(&pwm_lock); + guard(mutex)(&pwm_lock); + pwm = &chip->pwms[index]; err = pwm_device_request(pwm, label); if (err < 0) - pwm = ERR_PTR(err); + return ERR_PTR(err); - mutex_unlock(&pwm_lock); return pwm; } @@ -1102,11 +1094,11 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) chip->owner = owner; - mutex_lock(&pwm_lock); + guard(mutex)(&pwm_lock); ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL); if (ret < 0) - goto err_idr_alloc; + return ret; chip->id = ret; @@ -1119,8 +1111,6 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) if (ret) goto err_device_add; - mutex_unlock(&pwm_lock); - return 0; err_device_add: @@ -1128,9 +1118,6 @@ err_device_add: of_pwmchip_remove(chip); idr_remove(&pwm_chips, chip->id); -err_idr_alloc: - - mutex_unlock(&pwm_lock); return ret; } @@ -1149,11 +1136,8 @@ void pwmchip_remove(struct pwm_chip *chip) if (IS_ENABLED(CONFIG_OF)) of_pwmchip_remove(chip); - mutex_lock(&pwm_lock); - - idr_remove(&pwm_chips, chip->id); - - mutex_unlock(&pwm_lock); + scoped_guard(mutex, &pwm_lock) + idr_remove(&pwm_chips, chip->id); device_del(&chip->dev); } @@ -1209,15 +1193,11 @@ static struct pwm_chip *fwnode_to_pwmchip(struct fwnode_handle *fwnode) struct pwm_chip *chip; unsigned long id, tmp; - mutex_lock(&pwm_lock); + guard(mutex)(&pwm_lock); idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) - if (pwmchip_parent(chip) && device_match_fwnode(pwmchip_parent(chip), fwnode)) { - mutex_unlock(&pwm_lock); + if (pwmchip_parent(chip) && device_match_fwnode(pwmchip_parent(chip), fwnode)) return chip; - } - - mutex_unlock(&pwm_lock); return ERR_PTR(-EPROBE_DEFER); } @@ -1532,11 +1512,11 @@ void pwm_put(struct pwm_device *pwm) chip = pwm->chip; - mutex_lock(&pwm_lock); + guard(mutex)(&pwm_lock); if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { pr_warn("PWM device already freed\n"); - goto out; + return; } if (chip->ops->free) @@ -1547,8 +1527,6 @@ void pwm_put(struct pwm_device *pwm) put_device(&chip->dev); module_put(chip->owner); -out: - mutex_unlock(&pwm_lock); } EXPORT_SYMBOL_GPL(pwm_put); -- cgit v1.2.3-58-ga151 From 4c50c71c6995a2546eedc36f8bd99fadd7883a46 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 27 Jun 2024 22:31:20 +0200 Subject: pwm: Use guards for export->lock instead of explicity mutex_lock + mutex_unlock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the compiler caring for unlocking the mutex several functions can be simplified. Benefit from that. There is just one caller left for mutex_lock(&export->lock). The code flow is too complicated there to convert it to the compiler assisted variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/210010f2e579a92476462726e18e0135f6854909.1719520143.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index bce42fbbe2c3..3df8397d7b2b 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -503,11 +503,11 @@ static ssize_t period_store(struct device *pwm_dev, if (ret) return ret; - mutex_lock(&export->lock); + guard(mutex)(&export->lock); + pwm_get_state(pwm, &state); state.period = val; ret = pwm_apply_might_sleep(pwm, &state); - mutex_unlock(&export->lock); return ret ? : size; } @@ -538,11 +538,11 @@ static ssize_t duty_cycle_store(struct device *pwm_dev, if (ret) return ret; - mutex_lock(&export->lock); + guard(mutex)(&export->lock); + pwm_get_state(pwm, &state); state.duty_cycle = val; ret = pwm_apply_might_sleep(pwm, &state); - mutex_unlock(&export->lock); return ret ? : size; } @@ -572,7 +572,7 @@ static ssize_t enable_store(struct device *pwm_dev, if (ret) return ret; - mutex_lock(&export->lock); + guard(mutex)(&export->lock); pwm_get_state(pwm, &state); @@ -584,14 +584,11 @@ static ssize_t enable_store(struct device *pwm_dev, state.enabled = true; break; default: - ret = -EINVAL; - goto unlock; + return -EINVAL; } ret = pwm_apply_might_sleep(pwm, &state); -unlock: - mutex_unlock(&export->lock); return ret ? : size; } @@ -635,11 +632,11 @@ static ssize_t polarity_store(struct device *pwm_dev, else return -EINVAL; - mutex_lock(&export->lock); + guard(mutex)(&export->lock); + pwm_get_state(pwm, &state); state.polarity = polarity; ret = pwm_apply_might_sleep(pwm, &state); - mutex_unlock(&export->lock); return ret ? : size; } -- cgit v1.2.3-58-ga151 From 0007fa1292a24ef23d662ec7afe178088ab7766d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 27 Jun 2024 22:31:21 +0200 Subject: pwm: Use guards for pwm_lookup_lock instead of explicity mutex_lock + mutex_unlock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the compiler caring for unlocking the mutex several functions can be simplified. Benefit from that. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/28807cb5d9dbce66860f74829c0f57cd9c01373e.1719520143.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 51 ++++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 3df8397d7b2b..cf6d91f1a9e6 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -1343,14 +1343,12 @@ static LIST_HEAD(pwm_lookup_list); */ void pwm_add_table(struct pwm_lookup *table, size_t num) { - mutex_lock(&pwm_lookup_lock); + guard(mutex)(&pwm_lookup_lock); while (num--) { list_add_tail(&table->list, &pwm_lookup_list); table++; } - - mutex_unlock(&pwm_lookup_lock); } /** @@ -1360,14 +1358,12 @@ void pwm_add_table(struct pwm_lookup *table, size_t num) */ void pwm_remove_table(struct pwm_lookup *table, size_t num) { - mutex_lock(&pwm_lookup_lock); + guard(mutex)(&pwm_lookup_lock); while (num--) { list_del(&table->list); table++; } - - mutex_unlock(&pwm_lookup_lock); } /** @@ -1428,36 +1424,33 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) * Then we take the most specific entry - with the following order * of precedence: dev+con > dev only > con only. */ - mutex_lock(&pwm_lookup_lock); + scoped_guard(mutex, &pwm_lookup_lock) + list_for_each_entry(p, &pwm_lookup_list, list) { + match = 0; - list_for_each_entry(p, &pwm_lookup_list, list) { - match = 0; + if (p->dev_id) { + if (!dev_id || strcmp(p->dev_id, dev_id)) + continue; - if (p->dev_id) { - if (!dev_id || strcmp(p->dev_id, dev_id)) - continue; + match += 2; + } - match += 2; - } + if (p->con_id) { + if (!con_id || strcmp(p->con_id, con_id)) + continue; - if (p->con_id) { - if (!con_id || strcmp(p->con_id, con_id)) - continue; + match += 1; + } - match += 1; - } + if (match > best) { + chosen = p; - if (match > best) { - chosen = p; - - if (match != 3) - best = match; - else - break; + if (match != 3) + best = match; + else + break; + } } - } - - mutex_unlock(&pwm_lookup_lock); if (!chosen) return ERR_PTR(-ENODEV); -- cgit v1.2.3-58-ga151 From 14b9dc66e93abbd16b22ac9153f658de1acaaf49 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 28 Jun 2024 08:35:23 +0200 Subject: pwm: xilinx: Simplify using devm_ functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are devm variants for clk_prepare_enable() and pwmchip_add(); and clk_prepare_enable() can be done together with devm_clk_get(). This allows to simplify the error paths in .probe() and drop .remove() completely. With the remove callback gone, the last user of platform_get_drvdata() is gone and so the call to platform_set_drvdata() can be dropped, too. Signed-off-by: Uwe Kleine-König Reviewed-by: Sean Anderson Link: https://lore.kernel.org/r/20240628063524.92907-2-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-xilinx.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/drivers/pwm/pwm-xilinx.c b/drivers/pwm/pwm-xilinx.c index 3a7deebb0d0c..52c241982807 100644 --- a/drivers/pwm/pwm-xilinx.c +++ b/drivers/pwm/pwm-xilinx.c @@ -224,7 +224,6 @@ static int xilinx_pwm_probe(struct platform_device *pdev) if (IS_ERR(chip)) return PTR_ERR(chip); priv = xilinx_pwm_chip_to_priv(chip); - platform_set_drvdata(pdev, chip); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) @@ -263,37 +262,24 @@ static int xilinx_pwm_probe(struct platform_device *pdev) * alas, such properties are not allowed to be used. */ - priv->clk = devm_clk_get(dev, "s_axi_aclk"); + priv->clk = devm_clk_get_enabled(dev, "s_axi_aclk"); if (IS_ERR(priv->clk)) return dev_err_probe(dev, PTR_ERR(priv->clk), "Could not get clock\n"); - ret = clk_prepare_enable(priv->clk); + ret = devm_clk_rate_exclusive_get(dev, priv->clk); if (ret) - return dev_err_probe(dev, ret, "Clock enable failed\n"); - clk_rate_exclusive_get(priv->clk); + return dev_err_probe(dev, ret, + "Failed to lock clock rate\n"); chip->ops = &xilinx_pwm_ops; - ret = pwmchip_add(chip); - if (ret) { - clk_rate_exclusive_put(priv->clk); - clk_disable_unprepare(priv->clk); + ret = devm_pwmchip_add(dev, chip); + if (ret) return dev_err_probe(dev, ret, "Could not register PWM chip\n"); - } return 0; } -static void xilinx_pwm_remove(struct platform_device *pdev) -{ - struct pwm_chip *chip = platform_get_drvdata(pdev); - struct xilinx_timer_priv *priv = xilinx_pwm_chip_to_priv(chip); - - pwmchip_remove(chip); - clk_rate_exclusive_put(priv->clk); - clk_disable_unprepare(priv->clk); -} - static const struct of_device_id xilinx_pwm_of_match[] = { { .compatible = "xlnx,xps-timer-1.00.a", }, {}, @@ -302,7 +288,6 @@ MODULE_DEVICE_TABLE(of, xilinx_pwm_of_match); static struct platform_driver xilinx_pwm_driver = { .probe = xilinx_pwm_probe, - .remove_new = xilinx_pwm_remove, .driver = { .name = "xilinx-pwm", .of_match_table = of_match_ptr(xilinx_pwm_of_match), -- cgit v1.2.3-58-ga151 From 9dd42d019e6399e6e7d9e90759a61ff430625285 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 28 Jun 2024 12:35:19 +0200 Subject: pwm: Allow pwm state transitions from an invalid state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While driving a PWM via the sysfs API it's hard to determine the right order of writes to the pseudo files "period" and "duty_cycle": If you want to go from duty_cycle/period = 50/100 to 150/300 you have to write period first (because 150/100 is invalid). If however you start at 400/500 the duty_cycle must be configured first. The rule that works is: If you increase period write period first, otherwise write duty_cycle first. A complication however is that it's usually sensible to configure the polarity before both period and duty_cycle. This can only be done if the current state's duty_cycle and period configuration isn't bogus though. It is still worse (but I think only theoretic) if you have a PWM that only supports inverted polarity and you start with period = 0 and polarity = normal. Then you can change neither period (because polarity = normal is refused) nor polarity (because there is still period = 0). To simplify the corner cases for userspace, let invalid target states pass if the current state is invalid already. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20240628103519.105020-2-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index cf6d91f1a9e6..8acbcf5b6673 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -137,6 +137,25 @@ static void pwm_apply_debug(struct pwm_device *pwm, } } +static bool pwm_state_valid(const struct pwm_state *state) +{ + /* + * For a disabled state all other state description is irrelevant and + * and supposed to be ignored. So also ignore any strange values and + * consider the state ok. + */ + if (state->enabled) + return true; + + if (!state->period) + return false; + + if (state->duty_cycle > state->period) + return false; + + return true; +} + /** * __pwm_apply() - atomically apply a new state to a PWM device * @pwm: PWM device @@ -147,10 +166,26 @@ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) struct pwm_chip *chip; int err; - if (!pwm || !state || !state->period || - state->duty_cycle > state->period) + if (!pwm || !state) return -EINVAL; + if (!pwm_state_valid(state)) { + /* + * Allow to transition from one invalid state to another. + * This ensures that you can e.g. change the polarity while + * the period is zero. (This happens on stm32 when the hardware + * is in its poweron default state.) This greatly simplifies + * working with the sysfs API where you can only change one + * parameter at a time. + */ + if (!pwm_state_valid(&pwm->state)) { + pwm->state = *state; + return 0; + } + + return -EINVAL; + } + chip = pwm->chip; if (state->period == pwm->state.period && -- cgit v1.2.3-58-ga151 From c1330cb9a56d42632b1af54cda374e01e39dcc59 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 9 Jul 2024 12:18:06 +0200 Subject: pwm: atmel-tcb: Simplify checking the companion output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two outputs provided by the supported hardware share some settings, so access to the other PWM is required when one of them is configured. Instead of an explicit if to deterimine the other PWM just use hwpwm ^ 1. Further atcbpwm is never NULL, so drop the corresponding check. Signed-off-by: Uwe Kleine-König Acked-by: Nicolas Ferre Link: https://lore.kernel.org/r/20240709101806.52394-4-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-atmel-tcb.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index aca11493239a..2feee3744b50 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -260,7 +260,8 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; - struct atmel_tcb_pwm_device *atcbpwm = NULL; + /* companion PWM sharing register values period and div */ + struct atmel_tcb_pwm_device *atcbpwm = &tcbpwmc->pwms[pwm->hwpwm ^ 1]; int i = 0; int slowclk = 0; unsigned period; @@ -305,11 +306,6 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, duty = div_u64(duty_ns, min); period = div_u64(period_ns, min); - if (pwm->hwpwm == 0) - atcbpwm = &tcbpwmc->pwms[1]; - else - atcbpwm = &tcbpwmc->pwms[0]; - /* * PWM devices provided by the TCB driver are grouped by 2. * PWM devices in a given group must be configured with the @@ -318,8 +314,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * We're checking the period value of the second PWM device * in this group before applying the new config. */ - if ((atcbpwm && atcbpwm->duty > 0 && - atcbpwm->duty != atcbpwm->period) && + if ((atcbpwm->duty > 0 && atcbpwm->duty != atcbpwm->period) && (atcbpwm->div != i || atcbpwm->period != period)) { dev_err(pwmchip_parent(chip), "failed to configure period_ns: PWM group already configured with a different value\n"); -- cgit v1.2.3-58-ga151 From c9a787b9456066d4bd34522f880bd16208d775dd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 9 Jul 2024 11:22:21 +0200 Subject: pwm: atmel-tcb: Make private data variable naming consistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the variables of type struct atmel_tcb_pwm_device are named "tcbpwm", and variables of type atmel_tcb_pwm_chip are either named "tcbpwm" (too!) or "tcbpwmc". Rename the chips with device name to "tcbpwmc" to get a consistent naming. Signed-off-by: Uwe Kleine-König Acked-by: Nicolas Ferre Link: https://lore.kernel.org/r/20240709092221.47025-2-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-atmel-tcb.c | 90 ++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 2feee3744b50..f9a9c12cbcdd 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -382,17 +382,17 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) { struct pwm_chip *chip; const struct of_device_id *match; - struct atmel_tcb_pwm_chip *tcbpwm; + struct atmel_tcb_pwm_chip *tcbpwmc; const struct atmel_tcb_config *config; struct device_node *np = pdev->dev.of_node; char clk_name[] = "t0_clk"; int err; int channel; - chip = devm_pwmchip_alloc(&pdev->dev, NPWM, sizeof(*tcbpwm)); + chip = devm_pwmchip_alloc(&pdev->dev, NPWM, sizeof(*tcbpwmc)); if (IS_ERR(chip)) return PTR_ERR(chip); - tcbpwm = to_tcb_chip(chip); + tcbpwmc = to_tcb_chip(chip); err = of_property_read_u32(np, "reg", &channel); if (err < 0) { @@ -402,20 +402,20 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) return err; } - tcbpwm->regmap = syscon_node_to_regmap(np->parent); - if (IS_ERR(tcbpwm->regmap)) - return PTR_ERR(tcbpwm->regmap); + tcbpwmc->regmap = syscon_node_to_regmap(np->parent); + if (IS_ERR(tcbpwmc->regmap)) + return PTR_ERR(tcbpwmc->regmap); - tcbpwm->slow_clk = of_clk_get_by_name(np->parent, "slow_clk"); - if (IS_ERR(tcbpwm->slow_clk)) - return PTR_ERR(tcbpwm->slow_clk); + tcbpwmc->slow_clk = of_clk_get_by_name(np->parent, "slow_clk"); + if (IS_ERR(tcbpwmc->slow_clk)) + return PTR_ERR(tcbpwmc->slow_clk); clk_name[1] += channel; - tcbpwm->clk = of_clk_get_by_name(np->parent, clk_name); - if (IS_ERR(tcbpwm->clk)) - tcbpwm->clk = of_clk_get_by_name(np->parent, "t0_clk"); - if (IS_ERR(tcbpwm->clk)) { - err = PTR_ERR(tcbpwm->clk); + tcbpwmc->clk = of_clk_get_by_name(np->parent, clk_name); + if (IS_ERR(tcbpwmc->clk)) + tcbpwmc->clk = of_clk_get_by_name(np->parent, "t0_clk"); + if (IS_ERR(tcbpwmc->clk)) { + err = PTR_ERR(tcbpwmc->clk); goto err_slow_clk; } @@ -423,22 +423,22 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) config = match->data; if (config->has_gclk) { - tcbpwm->gclk = of_clk_get_by_name(np->parent, "gclk"); - if (IS_ERR(tcbpwm->gclk)) { - err = PTR_ERR(tcbpwm->gclk); + tcbpwmc->gclk = of_clk_get_by_name(np->parent, "gclk"); + if (IS_ERR(tcbpwmc->gclk)) { + err = PTR_ERR(tcbpwmc->gclk); goto err_clk; } } chip->ops = &atmel_tcb_pwm_ops; - tcbpwm->channel = channel; - tcbpwm->width = config->counter_width; + tcbpwmc->channel = channel; + tcbpwmc->width = config->counter_width; - err = clk_prepare_enable(tcbpwm->slow_clk); + err = clk_prepare_enable(tcbpwmc->slow_clk); if (err) goto err_gclk; - spin_lock_init(&tcbpwm->lock); + spin_lock_init(&tcbpwmc->lock); err = pwmchip_add(chip); if (err < 0) @@ -449,16 +449,16 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) return 0; err_disable_clk: - clk_disable_unprepare(tcbpwm->slow_clk); + clk_disable_unprepare(tcbpwmc->slow_clk); err_gclk: - clk_put(tcbpwm->gclk); + clk_put(tcbpwmc->gclk); err_clk: - clk_put(tcbpwm->clk); + clk_put(tcbpwmc->clk); err_slow_clk: - clk_put(tcbpwm->slow_clk); + clk_put(tcbpwmc->slow_clk); return err; } @@ -466,14 +466,14 @@ err_slow_clk: static void atmel_tcb_pwm_remove(struct platform_device *pdev) { struct pwm_chip *chip = platform_get_drvdata(pdev); - struct atmel_tcb_pwm_chip *tcbpwm = to_tcb_chip(chip); + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); pwmchip_remove(chip); - clk_disable_unprepare(tcbpwm->slow_clk); - clk_put(tcbpwm->gclk); - clk_put(tcbpwm->clk); - clk_put(tcbpwm->slow_clk); + clk_disable_unprepare(tcbpwmc->slow_clk); + clk_put(tcbpwmc->gclk); + clk_put(tcbpwmc->clk); + clk_put(tcbpwmc->slow_clk); } static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { @@ -485,14 +485,14 @@ MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); static int atmel_tcb_pwm_suspend(struct device *dev) { struct pwm_chip *chip = dev_get_drvdata(dev); - struct atmel_tcb_pwm_chip *tcbpwm = to_tcb_chip(chip); - struct atmel_tcb_channel *chan = &tcbpwm->bkup; - unsigned int channel = tcbpwm->channel; + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_channel *chan = &tcbpwmc->bkup; + unsigned int channel = tcbpwmc->channel; - regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr); - regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), &chan->ra); - regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), &chan->rb); - regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), &chan->rc); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), &chan->ra); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), &chan->rb); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), &chan->rc); return 0; } @@ -500,17 +500,17 @@ static int atmel_tcb_pwm_suspend(struct device *dev) static int atmel_tcb_pwm_resume(struct device *dev) { struct pwm_chip *chip = dev_get_drvdata(dev); - struct atmel_tcb_pwm_chip *tcbpwm = to_tcb_chip(chip); - struct atmel_tcb_channel *chan = &tcbpwm->bkup; - unsigned int channel = tcbpwm->channel; + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_channel *chan = &tcbpwmc->bkup; + unsigned int channel = tcbpwmc->channel; - regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr); - regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), chan->ra); - regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), chan->rb); - regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), chan->rc); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), chan->ra); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), chan->rb); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), chan->rc); if (chan->enabled) - regmap_write(tcbpwm->regmap, + regmap_write(tcbpwmc->regmap, ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, ATMEL_TC_REG(channel, CCR)); -- cgit v1.2.3-58-ga151 From 20c0455f3cffc93ef2712f33a7d118d4e5483ef3 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 10 Jul 2024 18:36:51 +0200 Subject: dt-bindings: pwm: at91: Add sama7d65 compatible string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add compatible string for sama7d65. Like sama7g5, it currently binds to "atmel,sama5d2-pwm" compatibility string for this driver, so add an "enum" to reflect that. Signed-off-by: Nicolas Ferre Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240710163651.343751-1-nicolas.ferre@microchip.com Signed-off-by: Uwe Kleine-König --- Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml b/Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml index 96cd6f3c3546..d20ad27657aa 100644 --- a/Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml @@ -23,7 +23,9 @@ properties: - atmel,sama5d2-pwm - microchip,sam9x60-pwm - items: - - const: microchip,sama7g5-pwm + - enum: + - microchip,sama7d65-pwm + - microchip,sama7g5-pwm - const: atmel,sama5d2-pwm - items: - const: microchip,sam9x7-pwm -- cgit v1.2.3-58-ga151 From 240b129d597cb8a8880eb3a381ff10eb98ca0c07 Mon Sep 17 00:00:00 2001 From: Trevor Gamblin Date: Thu, 11 Jul 2024 08:57:42 -0400 Subject: pwm: axi-pwmgen: add .max_register to regmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was missed in the basic driver and is useful for debug, so add it. Example regmap output before the patch: |root@zed-tg:~# cat /sys/kernel/debug/regmap/44a60000.pwm/registers |0: 00020100 And with it: |root@zed-tg:~# cat /sys/kernel/debug/regmap/44a60000.pwm/registers |00: 00020100 |04: 00000000 |08: 00000000 |0c: 601a3471 |10: 00000000 |14: 00000002 |18: 00000001 |1c: 00000000 |... Signed-off-by: Trevor Gamblin Link: https://lore.kernel.org/r/20240711125743.3956935-1-tgamblin@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-axi-pwmgen.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pwm/pwm-axi-pwmgen.c b/drivers/pwm/pwm-axi-pwmgen.c index aac4f395497b..3ad60edf20a5 100644 --- a/drivers/pwm/pwm-axi-pwmgen.c +++ b/drivers/pwm/pwm-axi-pwmgen.c @@ -51,6 +51,7 @@ static const struct regmap_config axi_pwmgen_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, + .max_register = 0xFC, }; static int axi_pwmgen_apply(struct pwm_chip *chip, struct pwm_device *pwm, -- cgit v1.2.3-58-ga151