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