brintos

brintos / linux-shallow public Read only

0
0
Text · 13.0 KiB · bef59fc Raw
522 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * This file is part the core part STM32 DFSDM driver4 *5 * Copyright (C) 2017, STMicroelectronics - All Rights Reserved6 * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.7 */8 9#include <linux/bitfield.h>10#include <linux/clk.h>11#include <linux/iio/iio.h>12#include <linux/iio/sysfs.h>13#include <linux/interrupt.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/of_platform.h>17#include <linux/pinctrl/consumer.h>18#include <linux/platform_device.h>19#include <linux/pm_runtime.h>20#include <linux/regmap.h>21#include <linux/slab.h>22 23#include "stm32-dfsdm.h"24 25/**26 * struct stm32_dfsdm_dev_data - DFSDM compatible configuration data27 * @ipid: DFSDM identification number. Used only if hardware provides identification registers28 * @num_filters: DFSDM number of filters. Unused if identification registers are available29 * @num_channels: DFSDM number of channels. Unused if identification registers are available30 * @regmap_cfg: SAI register map configuration pointer31 */32struct stm32_dfsdm_dev_data {33	u32 ipid;34	unsigned int num_filters;35	unsigned int num_channels;36	const struct regmap_config *regmap_cfg;37};38 39#define STM32H7_DFSDM_NUM_FILTERS	440#define STM32H7_DFSDM_NUM_CHANNELS	841 42static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)43{44	if (reg < DFSDM_FILTER_BASE_ADR)45		return false;46 47	/*48	 * Mask is done on register to avoid to list registers of all49	 * filter instances.50	 */51	switch (reg & DFSDM_FILTER_REG_MASK) {52	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:53	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:54	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:55	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:56		return true;57	}58 59	return false;60}61 62static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {63	.reg_bits = 32,64	.val_bits = 32,65	.reg_stride = sizeof(u32),66	.max_register = 0x2B8,67	.volatile_reg = stm32_dfsdm_volatile_reg,68	.fast_io = true,69};70 71static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {72	.num_filters = STM32H7_DFSDM_NUM_FILTERS,73	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,74	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,75};76 77static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = {78	.reg_bits = 32,79	.val_bits = 32,80	.reg_stride = sizeof(u32),81	.max_register = 0x7fc,82	.volatile_reg = stm32_dfsdm_volatile_reg,83	.fast_io = true,84};85 86static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = {87	.ipid = STM32MP15_IPIDR_NUMBER,88	.regmap_cfg = &stm32mp1_dfsdm_regmap_cfg,89};90 91struct dfsdm_priv {92	struct platform_device *pdev; /* platform device */93 94	struct stm32_dfsdm dfsdm; /* common data exported for all instances */95 96	unsigned int spi_clk_out_div; /* SPI clkout divider value */97	atomic_t n_active_ch;	/* number of current active channels */98 99	struct clk *clk; /* DFSDM clock */100	struct clk *aclk; /* audio clock */101};102 103static inline struct dfsdm_priv *to_stm32_dfsdm_priv(struct stm32_dfsdm *dfsdm)104{105	return container_of(dfsdm, struct dfsdm_priv, dfsdm);106}107 108static int stm32_dfsdm_clk_prepare_enable(struct stm32_dfsdm *dfsdm)109{110	struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);111	int ret;112 113	ret = clk_prepare_enable(priv->clk);114	if (ret || !priv->aclk)115		return ret;116 117	ret = clk_prepare_enable(priv->aclk);118	if (ret)119		clk_disable_unprepare(priv->clk);120 121	return ret;122}123 124static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm)125{126	struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);127 128	clk_disable_unprepare(priv->aclk);129	clk_disable_unprepare(priv->clk);130}131 132/**133 * stm32_dfsdm_start_dfsdm - start global dfsdm interface.134 *135 * Enable interface if n_active_ch is not null.136 * @dfsdm: Handle used to retrieve dfsdm context.137 */138int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)139{140	struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);141	struct device *dev = &priv->pdev->dev;142	unsigned int clk_div = priv->spi_clk_out_div, clk_src;143	int ret;144 145	if (atomic_inc_return(&priv->n_active_ch) == 1) {146		ret = pm_runtime_resume_and_get(dev);147		if (ret < 0)148			goto error_ret;149 150		/* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */151		clk_src = priv->aclk ? 1 : 0;152		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),153					 DFSDM_CHCFGR1_CKOUTSRC_MASK,154					 DFSDM_CHCFGR1_CKOUTSRC(clk_src));155		if (ret < 0)156			goto pm_put;157 158		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */159		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),160					 DFSDM_CHCFGR1_CKOUTDIV_MASK,161					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));162		if (ret < 0)163			goto pm_put;164 165		/* Global enable of DFSDM interface */166		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),167					 DFSDM_CHCFGR1_DFSDMEN_MASK,168					 DFSDM_CHCFGR1_DFSDMEN(1));169		if (ret < 0)170			goto pm_put;171	}172 173	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,174		atomic_read(&priv->n_active_ch));175 176	return 0;177 178pm_put:179	pm_runtime_put_sync(dev);180error_ret:181	atomic_dec(&priv->n_active_ch);182 183	return ret;184}185EXPORT_SYMBOL_GPL(stm32_dfsdm_start_dfsdm);186 187/**188 * stm32_dfsdm_stop_dfsdm - stop global DFSDM interface.189 *190 * Disable interface if n_active_ch is null191 * @dfsdm: Handle used to retrieve dfsdm context.192 */193int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)194{195	struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);196	int ret;197 198	if (atomic_dec_and_test(&priv->n_active_ch)) {199		/* Global disable of DFSDM interface */200		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),201					 DFSDM_CHCFGR1_DFSDMEN_MASK,202					 DFSDM_CHCFGR1_DFSDMEN(0));203		if (ret < 0)204			return ret;205 206		/* Stop SPI CLKOUT */207		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),208					 DFSDM_CHCFGR1_CKOUTDIV_MASK,209					 DFSDM_CHCFGR1_CKOUTDIV(0));210		if (ret < 0)211			return ret;212 213		pm_runtime_put_sync(&priv->pdev->dev);214	}215	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,216		atomic_read(&priv->n_active_ch));217 218	return 0;219}220EXPORT_SYMBOL_GPL(stm32_dfsdm_stop_dfsdm);221 222static int stm32_dfsdm_parse_of(struct platform_device *pdev,223				struct dfsdm_priv *priv)224{225	struct device_node *node = pdev->dev.of_node;226	struct resource *res;227	unsigned long clk_freq, divider;228	unsigned int spi_freq, rem;229	int ret;230 231	if (!node)232		return -EINVAL;233 234	priv->dfsdm.base = devm_platform_get_and_ioremap_resource(pdev, 0,235							&res);236	if (IS_ERR(priv->dfsdm.base))237		return PTR_ERR(priv->dfsdm.base);238 239	priv->dfsdm.phys_base = res->start;240 241	/*242	 * "dfsdm" clock is mandatory for DFSDM peripheral clocking.243	 * "dfsdm" or "audio" clocks can be used as source clock for244	 * the SPI clock out signal and internal processing, depending245	 * on use case.246	 */247	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");248	if (IS_ERR(priv->clk))249		return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk),250				     "Failed to get clock\n");251 252	priv->aclk = devm_clk_get(&pdev->dev, "audio");253	if (IS_ERR(priv->aclk))254		priv->aclk = NULL;255 256	if (priv->aclk)257		clk_freq = clk_get_rate(priv->aclk);258	else259		clk_freq = clk_get_rate(priv->clk);260 261	/* SPI clock out frequency */262	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",263				   &spi_freq);264	if (ret < 0) {265		/* No SPI master mode */266		return 0;267	}268 269	divider = div_u64_rem(clk_freq, spi_freq, &rem);270	/* Round up divider when ckout isn't precise, not to exceed spi_freq */271	if (rem)272		divider++;273 274	/* programmable divider is in range of [2:256] */275	if (divider < 2 || divider > 256) {276		dev_err(&pdev->dev, "spi-max-frequency not achievable\n");277		return -EINVAL;278	}279 280	/* SPI clock output divider is: divider = CKOUTDIV + 1 */281	priv->spi_clk_out_div = divider - 1;282	priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1);283 284	if (rem) {285		dev_warn(&pdev->dev, "SPI clock not accurate\n");286		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",287			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);288	}289 290	return 0;291};292 293static const struct of_device_id stm32_dfsdm_of_match[] = {294	{295		.compatible = "st,stm32h7-dfsdm",296		.data = &stm32h7_dfsdm_data,297	},298	{299		.compatible = "st,stm32mp1-dfsdm",300		.data = &stm32mp1_dfsdm_data,301	},302	{ }303};304MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);305 306static int stm32_dfsdm_probe_identification(struct platform_device *pdev,307					    struct dfsdm_priv *priv,308					    const struct stm32_dfsdm_dev_data *dev_data)309{310	struct device_node *np = pdev->dev.of_node;311	struct device_node *child;312	struct stm32_dfsdm *dfsdm = &priv->dfsdm;313	const char *compat;314	int ret, count = 0;315	u32 id, val;316 317	if (!dev_data->ipid) {318		dfsdm->num_fls = dev_data->num_filters;319		dfsdm->num_chs = dev_data->num_channels;320		return 0;321	}322 323	ret = regmap_read(dfsdm->regmap, DFSDM_IPIDR, &id);324	if (ret)325		return ret;326 327	if (id != dev_data->ipid) {328		dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id);329		return -EINVAL;330	}331 332	for_each_child_of_node(np, child) {333		ret = of_property_read_string(child, "compatible", &compat);334		if (ret)335			continue;336		/* Count only child nodes with dfsdm compatible */337		if (strstr(compat, "dfsdm"))338			count++;339	}340 341	ret = regmap_read(dfsdm->regmap, DFSDM_HWCFGR, &val);342	if (ret)343		return ret;344 345	dfsdm->num_fls = FIELD_GET(DFSDM_HWCFGR_NBF_MASK, val);346	dfsdm->num_chs = FIELD_GET(DFSDM_HWCFGR_NBT_MASK, val);347 348	if (count > dfsdm->num_fls) {349		dev_err(&pdev->dev, "Unexpected child number: %d", count);350		return -EINVAL;351	}352 353	ret = regmap_read(dfsdm->regmap, DFSDM_VERR, &val);354	if (ret)355		return ret;356 357	dev_dbg(&pdev->dev, "DFSDM version: %lu.%lu. %d channels/%d filters\n",358		FIELD_GET(DFSDM_VERR_MAJREV_MASK, val),359		FIELD_GET(DFSDM_VERR_MINREV_MASK, val),360		dfsdm->num_chs, dfsdm->num_fls);361 362	return 0;363}364 365static int stm32_dfsdm_probe(struct platform_device *pdev)366{367	struct dfsdm_priv *priv;368	const struct stm32_dfsdm_dev_data *dev_data;369	struct stm32_dfsdm *dfsdm;370	int ret;371 372	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);373	if (!priv)374		return -ENOMEM;375 376	priv->pdev = pdev;377 378	dev_data = of_device_get_match_data(&pdev->dev);379 380	dfsdm = &priv->dfsdm;381 382	ret = stm32_dfsdm_parse_of(pdev, priv);383	if (ret < 0)384		return ret;385 386	dfsdm->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dfsdm",387						  dfsdm->base,388						  dev_data->regmap_cfg);389	if (IS_ERR(dfsdm->regmap)) {390		ret = PTR_ERR(dfsdm->regmap);391		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",392			__func__, ret);393		return ret;394	}395 396	ret = stm32_dfsdm_probe_identification(pdev, priv, dev_data);397	if (ret < 0)398		return ret;399 400	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dfsdm->num_fls,401				      sizeof(*dfsdm->fl_list), GFP_KERNEL);402	if (!dfsdm->fl_list)403		return -ENOMEM;404 405	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dfsdm->num_chs,406				      sizeof(*dfsdm->ch_list), GFP_KERNEL);407	if (!dfsdm->ch_list)408		return -ENOMEM;409 410	platform_set_drvdata(pdev, dfsdm);411 412	ret = stm32_dfsdm_clk_prepare_enable(dfsdm);413	if (ret) {414		dev_err(&pdev->dev, "Failed to start clock\n");415		return ret;416	}417 418	pm_runtime_get_noresume(&pdev->dev);419	pm_runtime_set_active(&pdev->dev);420	pm_runtime_enable(&pdev->dev);421 422	ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);423	if (ret)424		goto pm_put;425 426	pm_runtime_put(&pdev->dev);427 428	return 0;429 430pm_put:431	pm_runtime_disable(&pdev->dev);432	pm_runtime_set_suspended(&pdev->dev);433	pm_runtime_put_noidle(&pdev->dev);434	stm32_dfsdm_clk_disable_unprepare(dfsdm);435 436	return ret;437}438 439static void stm32_dfsdm_core_remove(struct platform_device *pdev)440{441	struct stm32_dfsdm *dfsdm = platform_get_drvdata(pdev);442 443	pm_runtime_get_sync(&pdev->dev);444	of_platform_depopulate(&pdev->dev);445	pm_runtime_disable(&pdev->dev);446	pm_runtime_set_suspended(&pdev->dev);447	pm_runtime_put_noidle(&pdev->dev);448	stm32_dfsdm_clk_disable_unprepare(dfsdm);449}450 451static int stm32_dfsdm_core_suspend(struct device *dev)452{453	struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);454	struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);455	int ret;456 457	ret = pm_runtime_force_suspend(dev);458	if (ret)459		return ret;460 461	/* Balance devm_regmap_init_mmio_clk() clk_prepare() */462	clk_unprepare(priv->clk);463 464	return pinctrl_pm_select_sleep_state(dev);465}466 467static int stm32_dfsdm_core_resume(struct device *dev)468{469	struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);470	struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);471	int ret;472 473	ret = pinctrl_pm_select_default_state(dev);474	if (ret)475		return ret;476 477	ret = clk_prepare(priv->clk);478	if (ret)479		return ret;480 481	return pm_runtime_force_resume(dev);482}483 484static int stm32_dfsdm_core_runtime_suspend(struct device *dev)485{486	struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);487 488	stm32_dfsdm_clk_disable_unprepare(dfsdm);489 490	return 0;491}492 493static int stm32_dfsdm_core_runtime_resume(struct device *dev)494{495	struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);496 497	return stm32_dfsdm_clk_prepare_enable(dfsdm);498}499 500static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = {501	SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend, stm32_dfsdm_core_resume)502	RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend,503		       stm32_dfsdm_core_runtime_resume,504		       NULL)505};506 507static struct platform_driver stm32_dfsdm_driver = {508	.probe = stm32_dfsdm_probe,509	.remove_new = stm32_dfsdm_core_remove,510	.driver = {511		.name = "stm32-dfsdm",512		.of_match_table = stm32_dfsdm_of_match,513		.pm = pm_ptr(&stm32_dfsdm_core_pm_ops),514	},515};516 517module_platform_driver(stm32_dfsdm_driver);518 519MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");520MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");521MODULE_LICENSE("GPL v2");522