On Tue, 02 Jan 2018, Ryder Lee wrote: > Add a common driver for the top block of the MediaTek audio subsystem. > This is a wrapper which manages resources for audio components. > > Signed-off-by: Ryder Lee <ryder.lee@xxxxxxxxxxxx> > --- > drivers/mfd/Kconfig | 9 ++++ > drivers/mfd/Makefile | 2 + > drivers/mfd/mtk-audsys.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 149 insertions(+) > create mode 100644 drivers/mfd/mtk-audsys.c > > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig > index 1d20a80..ea50b51 100644 > --- a/drivers/mfd/Kconfig > +++ b/drivers/mfd/Kconfig > @@ -368,6 +368,15 @@ config MFD_MC13XXX_I2C > help > Select this if your MC13xxx is connected via an I2C bus. > > +config MFD_MEDIATEK_AUDSYS > + tristate "MediaTek audio subsystem interface" > + select MDF_CORE > + select REGMAP_MMIO > + help > + Select this if you have a audio subsystem in MediaTek SoC. > + The audio subsystem has at least a clock driver part and some > + audio components. > + > config MFD_MXS_LRADC > tristate "Freescale i.MX23/i.MX28 LRADC" > depends on ARCH_MXS || COMPILE_TEST > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile > index d9474ad..3e20927 100644 > --- a/drivers/mfd/Makefile > +++ b/drivers/mfd/Makefile > @@ -101,6 +101,8 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o > obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o > obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o > > +obj-$(CONFIG_MFD_MEDIATEK_AUDSYS) += mtk-audsys.o > + > obj-$(CONFIG_MFD_CORE) += mfd-core.o > > obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o > diff --git a/drivers/mfd/mtk-audsys.c b/drivers/mfd/mtk-audsys.c > new file mode 100644 > index 0000000..89399e1 > --- /dev/null > +++ b/drivers/mfd/mtk-audsys.c > @@ -0,0 +1,138 @@ > +/* > + * Mediatek audio subsystem core driver > + * > + * Copyright (c) 2017 MediaTek Inc. > + * > + * Author: Ryder Lee <ryder.lee@xxxxxxxxxxxx> > + * > + * For licencing details see kernel-base/COPYING You can't do that. Grep for SPDX to see what is expected. > + * > + */ > + > +#include <linux/clk.h> > +#include <linux/module.h> > +#include <linux/of_platform.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > + > +#define AUDSYS_MAX_CLK_NUM 3 When is this not 3? > +struct sys_dev { > + struct device *dev; > + struct regmap *regmap; > + int clk_num; > + struct clk *clks[]; > +}; > + > +static const struct regmap_config aud_regmap_config = { > + .reg_bits = 32, > + .reg_stride = 4, > + .val_bits = 32, > + .max_register = 0x15e0, > + .cache_type = REGCACHE_NONE, > +}; > + > +static int mtk_subsys_enable(struct sys_dev *sys) > +{ > + struct device *dev = sys->dev; I would remove dev and regmap from the sys_dev struct and pass in pdev directly into this function. Then use platform_get_drvdata() as you did in .remove(). > + struct clk *clk; > + int i, ret; > + > + for (i = 0; i < sys->clk_num; i++) { > + clk = of_clk_get(dev->of_node, i); > + if (IS_ERR(clk)) { > + if (PTR_ERR(clk) == -EPROBE_DEFER) > + return -EPROBE_DEFER; > + break; > + } > + sys->clks[i] = clk; > + } > + > + for (i = 0; i < sys->clk_num && sys->clks[i]; i++) { Why do you need a separate loop for this? Just prepare and enable valid clocks in the for() loop above. > + ret = clk_prepare_enable(sys->clks[i]); > + if (ret) > + goto err_enable_clks; > + } > + > + return 0; > + > +err_enable_clks: > + while (--i >= 0) > + clk_disable_unprepare(sys->clks[i]); > + > + return ret; > +} > + > +static int mtk_subsys_probe(struct platform_device *pdev) > +{ > + struct sys_dev *sys; > + struct resource *res; > + void __iomem *mmio; > + int num, ret; > + > + num = (int)of_device_get_match_data(&pdev->dev); > + if (!num) > + return -EINVAL; This is a very rigid method of achieving your aim. Please find a way to make this more dynamic. You're probably better off counting the elements within the property, checking to ensure there aren't more than the maximum pre-allocated/allowed clocks, then using the number gleaned directly from the Device Tree. > + sys = devm_kzalloc(&pdev->dev, sizeof(*sys) + > + sizeof(struct clk *) * num, GFP_KERNEL); You need to add bracketing here for clarity. > + if (!sys) > + return -ENOMEM; > + > + sys->clk_num = num; > + sys->dev = &pdev->dev; Why are you saving the device pointer? > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + mmio = devm_ioremap_resource(sys->dev, res); > + if (IS_ERR(mmio)) > + return PTR_ERR(mmio); > + > + sys->regmap = devm_regmap_init_mmio(sys->dev, mmio, > + &aud_regmap_config); Why are you saving a devm'ed regmap pointer? > + if (IS_ERR(sys->regmap)) > + return PTR_ERR(sys->regmap); > + > + platform_set_drvdata(pdev, sys); > + > + /* Enable top level clocks */ > + ret = mtk_subsys_enable(sys); mtk_subsys_enable_clks() > + if (ret) > + return ret; > + > + return devm_of_platform_populate(sys->dev); > +}; > + > +static int mtk_subsys_remove(struct platform_device *pdev) > +{ > + struct sys_dev *sys = platform_get_drvdata(pdev); > + int i; > + > + for (i = sys->clk_num - 1; i >= 0; i--) > + if (sys->clks[i]) This check is superfluous as the clk subsystem does this for you. > + clk_disable_unprepare(sys->clks[i]); > + > + return 0; > +} > + > +static const struct of_device_id of_match_audsys[] = { > + { > + .compatible = "mediatek,mt2701-audsys-core", > + .data = (void *)AUDSYS_MAX_CLK_NUM, You can remove this line. > + }, > + {}, > +}; > +MODULE_DEVICE_TABLE(platform, of_match_audsys); > + > +static struct platform_driver audsys_drv = { > + .probe = mtk_subsys_probe, > + .remove = mtk_subsys_remove, > + .driver = { > + .name = "mediatek-audsys-core", > + .of_match_table = of_match_ptr(of_match_audsys), > + }, > +}; > + > +builtin_platform_driver(audsys_drv); > + > +MODULE_DESCRIPTION("Mediatek audio subsystem core driver"); > +MODULE_LICENSE("GPL"); <just_checking> Are you sure this is what you want? </just_checking> -- Lee Jones Linaro Services Technical 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