// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics 2016
*
* Author: Benjamin Gaignard <benjamin.gaignard@st.com>
*
*/
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/timer/stm32-timer-trigger.h>
#include <linux/iio/trigger.h>
#include <linux/mfd/stm32-timers.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#define MAX_TRIGGERS 7
#define MAX_VALIDS 5
/* List the triggers created by each timer */
static const void *triggers_table[][MAX_TRIGGERS] = {
{ TIM1_TRGO, TIM1_TRGO2, TIM1_CH1, TIM1_CH2, TIM1_CH3, TIM1_CH4,},
{ TIM2_TRGO, TIM2_CH1, TIM2_CH2, TIM2_CH3, TIM2_CH4,},
{ TIM3_TRGO, TIM3_CH1, TIM3_CH2, TIM3_CH3, TIM3_CH4,},
{ TIM4_TRGO, TIM4_CH1, TIM4_CH2, TIM4_CH3, TIM4_CH4,},
{ TIM5_TRGO, TIM5_CH1, TIM5_CH2, TIM5_CH3, TIM5_CH4,},
{ TIM6_TRGO,},
{ TIM7_TRGO,},
{ TIM8_TRGO, TIM8_TRGO2, TIM8_CH1, TIM8_CH2, TIM8_CH3, TIM8_CH4,},
{ TIM9_TRGO, TIM9_CH1, TIM9_CH2,},
{ TIM10_OC1,},
{ TIM11_OC1,},
{ TIM12_TRGO, TIM12_CH1, TIM12_CH2,},
{ TIM13_OC1,},
{ TIM14_OC1,},
{ TIM15_TRGO,},
{ TIM16_OC1,},
{ TIM17_OC1,},
};
/* List the triggers accepted by each timer */
static const void *valids_table[][MAX_VALIDS] = {
{ TIM5_TRGO, TIM2_TRGO, TIM3_TRGO, TIM4_TRGO,},
{ TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,},
{ TIM1_TRGO, TIM2_TRGO, TIM5_TRGO, TIM4_TRGO,},
{ TIM1_TRGO, TIM2_TRGO, TIM3_TRGO, TIM8_TRGO,},
{ TIM2_TRGO, TIM3_TRGO, TIM4_TRGO, TIM8_TRGO,},
{ }, /* timer 6 */
{ }, /* timer 7 */
{ TIM1_TRGO, TIM2_TRGO, TIM4_TRGO, TIM5_TRGO,},
{ TIM2_TRGO, TIM3_TRGO, TIM10_OC1, TIM11_OC1,},
{ }, /* timer 10 */
{ }, /* timer 11 */
{ TIM4_TRGO, TIM5_TRGO, TIM13_OC1, TIM14_OC1,},
};
static const void *stm32h7_valids_table[][MAX_VALIDS] = {
{ TIM15_TRGO, TIM2_TRGO, TIM3_TRGO, TIM4_TRGO,},
{ TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,},
{ TIM1_TRGO, TIM2_TRGO, TIM15_TRGO, TIM4_TRGO,},
{ TIM1_TRGO, TIM2_TRGO, TIM3_TRGO, TIM8_TRGO,},
{ TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,},
{ }, /* timer 6 */
{ }, /* timer 7 */
{ TIM1_TRGO, TIM2_TRGO, TIM4_TRGO, TIM5_TRGO,},
{ }, /* timer 9 */
{ }, /* timer 10 */
{ }, /* timer 11 */
{ TIM4_TRGO, TIM5_TRGO, TIM13_OC1, TIM14_OC1,},
{ }, /* timer 13 */
{ }, /* timer 14 */
{ TIM1_TRGO, TIM3_TRGO, TIM16_OC1, TIM17_OC1,},
{ }, /* timer 16 */
{ }, /* timer 17 */
};
struct stm32_timer_trigger_regs {
u32 cr1;
u32 cr2;
u32 psc;
u32 arr;
u32 cnt;
u32 smcr;
};
struct stm32_timer_trigger {
struct device *dev;
struct regmap *regmap;
struct clk *clk;
bool enabled;
u32 max_arr;
const void *triggers;
const void *valids;
bool has_trgo2;
struct mutex lock; /* concurrent sysfs configuration */
struct list_head tr_list;
struct stm32_timer_trigger_regs bak;
};
struct stm32_timer_trigger_cfg {
const void *(*valids_table)[MAX_VALIDS];
const unsigned int num_valids_table;
};
static bool stm32_timer_is_trgo2_name(const char *name)
{
return !!strstr(name, "trgo2");
}
static bool stm32_timer_is_trgo_name(const char *name)
{
return (!!strstr(name, "trgo") && !strstr(name, "trgo2"));
}
static int stm32_timer_start(struct stm32_timer_trigger *priv,
struct iio_trigger *trig,
unsigned int frequency)
{
unsigned long long prd, div;
int prescaler = 0;
u32 ccer;
/* Period and prescaler values depends of clock rate */
div = (unsigned long long)clk_get_rate(priv->clk);
do_div(div, frequency);
prd = div;
/*
* Increase prescaler value until we get a result that fit
* with auto reload register maximum value.
*/
while (div > priv->max_arr) {
prescaler++;
div = prd;
do_div(div, (prescaler + 1));
}
prd = div;
if (prescaler > MAX_TIM_PSC) {
dev_err(priv->dev, "prescaler exceeds the maximum value\n");
return <