On Tue, 22 Nov 2016, Benjamin Gaignard wrote: > This hardware block could at used at same time for PWM generation > and IIO timer for other IPs like DAC, ADC or other timers. > PWM and IIO timer configuration are mixed in the same registers > so we need a MFD to be able to share those registers. > > Signed-off-by: Benjamin Gaignard <benjamin.gaignard@xxxxxx> > --- > drivers/mfd/Kconfig | 10 ++ > drivers/mfd/Makefile | 2 + > drivers/mfd/stm32-mfd-timer.c | 236 ++++++++++++++++++++++++++++++++++++ > include/linux/mfd/stm32-mfd-timer.h | 78 ++++++++++++ > 4 files changed, 326 insertions(+) > create mode 100644 drivers/mfd/stm32-mfd-timer.c > create mode 100644 include/linux/mfd/stm32-mfd-timer.h This driver is going to need a re-write. However, it's difficult to provide suggestions, since I've been left off of the Cc: list for all the other patches. Please re-send the set with all of the Maintainers Cc'ed on all of the patches. > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig > index c6df644..63aee36 100644 > --- a/drivers/mfd/Kconfig > +++ b/drivers/mfd/Kconfig > @@ -1607,6 +1607,15 @@ config MFD_STW481X > in various ST Microelectronics and ST-Ericsson embedded > Nomadik series. > > +config MFD_STM32_TIMER > + tristate "Support for STM32 multifunctions timer" > + select MFD_CORE > + select REGMAP > + depends on ARCH_STM32 > + depends on OF > + help > + Select multifunction driver (pwm, IIO trigger) for stm32 timers > + > menu "Multimedia Capabilities Port drivers" > depends on ARCH_SA1100 > > @@ -1644,4 +1653,5 @@ config MFD_VEXPRESS_SYSREG > on the ARM Ltd. Versatile Express board. > > endmenu > + > endif > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile > index 9834e66..b348c3e 100644 > --- a/drivers/mfd/Makefile > +++ b/drivers/mfd/Makefile > @@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o > obj-$(CONFIG_MFD_MT6397) += mt6397-core.o > > obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o > + > +obj-$(CONFIG_MFD_STM32_TIMER) += stm32-mfd-timer.o > diff --git a/drivers/mfd/stm32-mfd-timer.c b/drivers/mfd/stm32-mfd-timer.c > new file mode 100644 > index 0000000..67e7db3 > --- /dev/null > +++ b/drivers/mfd/stm32-mfd-timer.c > @@ -0,0 +1,236 @@ > +/* > + * stm32-timer.c > + * > + * Copyright (C) STMicroelectronics 2016 > + * Author: Benjamin Gaignard <benjamin.gaignard@xxxxxx> for STMicroelectronics. > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#include <linux/device.h> > +#include <linux/init.h> > +#include <linux/module.h> > +#include <linux/of.h> > + > +#include <linux/mfd/stm32-mfd-timer.h> > + > +static const struct stm32_mfd_timer_cfg mfd_cells_cfg[] = { > + { > + .pwm_name = "pwm1", > + .pwm_compatible = "st,stm32-pwm1", > + .trigger_name = "iiotimer1", > + .trigger_compatible = "st,stm32-iio-timer1", > + }, > + { > + .pwm_name = "pwm2", > + .pwm_compatible = "st,stm32-pwm2", > + .trigger_name = "iiotimer2", > + .trigger_compatible = "st,stm32-iio-timer2", > + }, > + { > + .pwm_name = "pwm3", > + .pwm_compatible = "st,stm32-pwm3", > + .trigger_name = "iiotimer3", > + .trigger_compatible = "st,stm32-iio-timer3", > + }, > + { > + .pwm_name = "pwm4", > + .pwm_compatible = "st,stm32-pwm4", > + .trigger_name = "iiotimer4", > + .trigger_compatible = "st,stm32-iio-timer4", > + }, > + { > + .pwm_name = "pwm5", > + .pwm_compatible = "st,stm32-pwm5", > + .trigger_name = "iiotimer5", > + .trigger_compatible = "st,stm32-iio-timer5", > + }, > + { > + .trigger_name = "iiotimer6", > + .trigger_compatible = "st,stm32-iio-timer6", > + }, > + { > + .trigger_name = "iiotimer7", > + .trigger_compatible = "st,stm32-iio-timer7", > + }, > + { > + .pwm_name = "pwm8", > + .pwm_compatible = "st,stm32-pwm8", > + .trigger_name = "iiotimer8", > + .trigger_compatible = "st,stm32-iio-timer8", > + }, > + { > + .pwm_name = "pwm9", > + .pwm_compatible = "st,stm32-pwm9", > + .trigger_name = "iiotimer9", > + .trigger_compatible = "st,stm32-iio-timer9", > + }, > + { > + .pwm_name = "pwm10", > + .pwm_compatible = "st,stm32-pwm10", > + }, > + { > + .pwm_name = "pwm11", > + .pwm_compatible = "st,stm32-pwm11", > + }, > + { > + .pwm_name = "pwm12", > + .pwm_compatible = "st,stm32-pwm12", > + .trigger_name = "iiotimer12", > + .trigger_compatible = "st,stm32-iio-timer12", > + }, > + { > + .pwm_name = "pwm13", > + .pwm_compatible = "st,stm32-pwm13", > + }, > + { > + .pwm_name = "pwm14", > + .pwm_compatible = "st,stm32-pwm14", > + }, > +}; > + > +static const struct of_device_id stm32_timer_of_match[] = { > + { > + .compatible = "st,stm32-mfd-timer1", > + .data = &mfd_cells_cfg[0], > + }, > + { > + .compatible = "st,stm32-mfd-timer2", > + .data = &mfd_cells_cfg[1], > + }, > + { > + .compatible = "st,stm32-mfd-timer3", > + .data = &mfd_cells_cfg[2], > + }, > + { > + .compatible = "st,stm32-mfd-timer4", > + .data = &mfd_cells_cfg[3], > + }, > + { > + .compatible = "st,stm32-mfd-timer5", > + .data = &mfd_cells_cfg[4], > + }, > + { > + .compatible = "st,stm32-mfd-timer6", > + .data = &mfd_cells_cfg[5], > + }, > + { > + .compatible = "st,stm32-mfd-timer7", > + .data = &mfd_cells_cfg[6], > + }, > + { > + .compatible = "st,stm32-mfd-timer8", > + .data = &mfd_cells_cfg[7], > + }, > + { > + .compatible = "st,stm32-mfd-timer9", > + .data = &mfd_cells_cfg[8], > + }, > + { > + .compatible = "st,stm32-mfd-timer10", > + .data = &mfd_cells_cfg[9], > + }, > + { > + .compatible = "st,stm32-mfd-timer11", > + .data = &mfd_cells_cfg[10], > + }, > + { > + .compatible = "st,stm32-mfd-timer12", > + .data = &mfd_cells_cfg[11], > + }, > + { > + .compatible = "st,stm32-mfd-timer13", > + .data = &mfd_cells_cfg[12], > + }, > + { > + .compatible = "st,stm32-mfd-timer14", > + .data = &mfd_cells_cfg[13], > + }, > +}; > + > +static const struct regmap_config stm32_timer_regmap_cfg = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = sizeof(u32), > + .max_register = 0x400, > + .fast_io = true, > +}; > + > +static int stm32_mfd_timer_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct device_node *np = dev->of_node; > + struct stm32_mfd_timer_dev *mfd; > + struct resource *res; > + int ret, nb_cells = 0; > + struct mfd_cell *cell = NULL; > + void __iomem *mmio; > + > + mfd = devm_kzalloc(dev, sizeof(*mfd), GFP_KERNEL); > + if (!mfd) > + return -ENOMEM; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) > + return -ENOMEM; > + > + mmio = devm_ioremap_resource(dev, res); > + if (IS_ERR(mmio)) > + return PTR_ERR(mmio); > + > + mfd->regmap = devm_regmap_init_mmio_clk(dev, "mfd_timer_clk", mmio, > + &stm32_timer_regmap_cfg); > + if (IS_ERR(mfd->regmap)) > + return PTR_ERR(mfd->regmap); > + > + mfd->clk = devm_clk_get(dev, NULL); > + if (IS_ERR(mfd->clk)) > + return PTR_ERR(mfd->clk); > + > + mfd->irq = platform_get_irq(pdev, 0); > + if (mfd->irq < 0) > + return -EINVAL; > + > + /* populate data structure depending on compatibility */ > + if (!of_match_node(stm32_timer_of_match, np)->data) > + return -EINVAL; > + > + mfd->cfg = > + (struct stm32_mfd_timer_cfg *)of_match_node(stm32_timer_of_match, np)->data; > + > + if (mfd->cfg->pwm_name && mfd->cfg->pwm_compatible) { > + cell = &mfd->cells[nb_cells++]; > + cell->name = mfd->cfg->pwm_name; > + cell->of_compatible = mfd->cfg->pwm_compatible; > + cell->platform_data = mfd; > + cell->pdata_size = sizeof(*mfd); > + } > + > + if (mfd->cfg->trigger_name && mfd->cfg->trigger_compatible) { > + cell = &mfd->cells[nb_cells++]; > + cell->name = mfd->cfg->trigger_name; > + cell->of_compatible = mfd->cfg->trigger_compatible; > + cell->platform_data = mfd; > + cell->pdata_size = sizeof(*mfd); > + } > + > + ret = devm_mfd_add_devices(&pdev->dev, pdev->id, mfd->cells, > + nb_cells, NULL, 0, NULL); > + if (ret) > + return ret; > + > + platform_set_drvdata(pdev, mfd); > + > + return 0; > +} > + > +static struct platform_driver stm32_mfd_timer_driver = { > + .probe = stm32_mfd_timer_probe, > + .driver = { > + .name = "stm32-mfd-timer", > + .of_match_table = stm32_timer_of_match, > + }, > +}; > +module_platform_driver(stm32_mfd_timer_driver); > + > +MODULE_DESCRIPTION("STMicroelectronics STM32 Timer MFD"); > +MODULE_LICENSE("GPL"); > diff --git a/include/linux/mfd/stm32-mfd-timer.h b/include/linux/mfd/stm32-mfd-timer.h > new file mode 100644 > index 0000000..4a79c22 > --- /dev/null > +++ b/include/linux/mfd/stm32-mfd-timer.h > @@ -0,0 +1,78 @@ > +/* > + * stm32-mfd-timer.h > + * > + * Copyright (C) STMicroelectronics 2016 > + * Author: Benjamin Gaignard <benjamin.gaignard@xxxxxx> for STMicroelectronics. > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#ifndef _LINUX_MFD_STM32_TIMER_H_ > +#define _LINUX_MFD_STM32_TIMER_H_ > + > +#include <linux/clk.h> > +#include <linux/mfd/core.h> > +#include <linux/regmap.h> > +#include <linux/reset.h> > + > +#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_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_CR1_CEN BIT(0) /* Counter Enable */ > +#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_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_SR_UIF BIT(0) /* Update 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_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_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12)) > +#define TIM_BDTR_BKE BIT(12) /* Break input enable */ > +#define TIM_BDTR_BKP BIT(13) /* Break input polarity */ > +#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */ > +#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */ > + > +#define STM32_TIMER_CELLS 2 > +#define MAX_TIM_PSC 0xFFFF > + > +struct stm32_mfd_timer_cfg { > + const char *pwm_name; > + const char *pwm_compatible; > + const char *trigger_name; > + const char *trigger_compatible; > +}; > + > +struct stm32_mfd_timer_dev { > + /* Device data */ > + struct device *dev; > + struct clk *clk; > + int irq; > + > + /* Registers mapping */ > + struct regmap *regmap; > + > + /* Private data */ > + struct mfd_cell cells[STM32_TIMER_CELLS]; > + struct stm32_mfd_timer_cfg *cfg; > +}; > + > +#endif -- Lee Jones Linaro STMicroelectronics Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html