Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Benjamin Gaignard | 2647 | 74.17% | 5 | 33.33% |
Fabrice Gasnier | 921 | 25.81% | 9 | 60.00% |
Jonathan Cameron | 1 | 0.03% | 1 | 6.67% |
Total | 3569 | 15 |
// 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 { struct device *dev; struct regmap *regmap; struct clk *clk; u32 max_arr; const void *triggers; const void *valids; bool has_trgo2; }; 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, cr1; /* 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 -EINVAL; } /* Check if nobody else use the timer */ regmap_read(priv->regmap, TIM_CCER, &ccer); if (ccer & TIM_CCER_CCXE) return -EBUSY; regmap_read(priv->regmap, TIM_CR1, &cr1); if (!(cr1 & TIM_CR1_CEN)) clk_enable(priv->clk); regmap_write(priv->regmap, TIM_PSC, prescaler); regmap_write(priv->regmap, TIM_ARR, prd - 1); regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); /* Force master mode to update mode */ if (stm32_timer_is_trgo2_name(trig->name)) regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2, 0x2 << TIM_CR2_MMS2_SHIFT); else regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS, 0x2 << TIM_CR2_MMS_SHIFT); /* Make sure that registers are updated */ regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); /* Enable controller */ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); return 0; } static void stm32_timer_stop(struct stm32_timer_trigger *priv) { u32 ccer, cr1; regmap_read(priv->regmap, TIM_CCER, &ccer); if (ccer & TIM_CCER_CCXE) return; regmap_read(priv->regmap, TIM_CR1, &cr1); if (cr1 & TIM_CR1_CEN) clk_disable(priv->clk); /* Stop timer */ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0); regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); regmap_write(priv->regmap, TIM_PSC, 0); regmap_write(priv->regmap, TIM_ARR, 0); /* Make sure that registers are updated */ regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); } static ssize_t stm32_tt_store_frequency(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct iio_trigger *trig = to_iio_trigger(dev); struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig); unsigned int freq; int ret; ret = kstrtouint(buf, 10, &freq); if (ret) return ret; if (freq == 0) { stm32_timer_stop(priv); } else { ret = stm32_timer_start(priv, trig, freq); if (ret) return ret; } return len; } static ssize_t stm32_tt_read_frequency(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_trigger *trig = to_iio_trigger(dev); struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig); u32 psc, arr, cr1; unsigned long long freq = 0; regmap_read(priv->regmap, TIM_CR1, &cr1); regmap_read(priv->regmap, TIM_PSC, &psc); regmap_read(priv->regmap, TIM_ARR, &arr); if (cr1 & TIM_CR1_CEN) { freq = (unsigned long long)clk_get_rate(priv->clk); do_div(freq, psc + 1); do_div(freq, arr + 1); } return sprintf(buf, "%d\n", (unsigned int)freq); } static IIO_DEV_ATTR_SAMP_FREQ(0660, stm32_tt_read_frequency, stm32_tt_store_frequency); #define MASTER_MODE_MAX 7 #define MASTER_MODE2_MAX 15 static char *master_mode_table[] = { "reset", "enable", "update", "compare_pulse", "OC1REF", "OC2REF", "OC3REF", "OC4REF", /* Master mode selection 2 only */ "OC5REF", "OC6REF", "compare_pulse_OC4REF", "compare_pulse_OC6REF", "compare_pulse_OC4REF_r_or_OC6REF_r", "compare_pulse_OC4REF_r_or_OC6REF_f", "compare_pulse_OC5REF_r_or_OC6REF_r", "compare_pulse_OC5REF_r_or_OC6REF_f", }; static ssize_t stm32_tt_show_master_mode(struct device *dev, struct device_attribute *attr, char *buf) { struct stm32_timer_trigger *priv = dev_get_drvdata(dev); struct iio_trigger *trig = to_iio_trigger(dev); u32 cr2; regmap_read(priv->regmap, TIM_CR2, &cr2); if (stm32_timer_is_trgo2_name(trig->name)) cr2 = (cr2 & TIM_CR2_MMS2) >> TIM_CR2_MMS2_SHIFT; else cr2 = (cr2 & TIM_CR2_MMS) >> TIM_CR2_MMS_SHIFT; return snprintf(buf, PAGE_SIZE, "%s\n", master_mode_table[cr2]); } static ssize_t stm32_tt_store_master_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct stm32_timer_trigger *priv = dev_get_drvdata(dev); struct iio_trigger *trig = to_iio_trigger(dev); u32 mask, shift, master_mode_max; int i; if (stm32_timer_is_trgo2_name(trig->name)) { mask = TIM_CR2_MMS2; shift = TIM_CR2_MMS2_SHIFT; master_mode_max = MASTER_MODE2_MAX; } else { mask = TIM_CR2_MMS; shift = TIM_CR2_MMS_SHIFT; master_mode_max = MASTER_MODE_MAX; } for (i = 0; i <= master_mode_max; i++) { if (!strncmp(master_mode_table[i], buf, strlen(master_mode_table[i]))) { regmap_update_bits(priv->regmap, TIM_CR2, mask, i << shift); /* Make sure that registers are updated */ regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); return len; } } return -EINVAL; } static ssize_t stm32_tt_show_master_mode_avail(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_trigger *trig = to_iio_trigger(dev); unsigned int i, master_mode_max; size_t len = 0; if (stm32_timer_is_trgo2_name(trig->name)) master_mode_max = MASTER_MODE2_MAX; else master_mode_max = MASTER_MODE_MAX; for (i = 0; i <= master_mode_max; i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", master_mode_table[i]); /* replace trailing space by newline */ buf[len - 1] = '\n'; return len; } static IIO_DEVICE_ATTR(master_mode_available, 0444, stm32_tt_show_master_mode_avail, NULL, 0); static IIO_DEVICE_ATTR(master_mode, 0660, stm32_tt_show_master_mode, stm32_tt_store_master_mode, 0); static struct attribute *stm32_trigger_attrs[] = { &iio_dev_attr_sampling_frequency.dev_attr.attr, &iio_dev_attr_master_mode.dev_attr.attr, &iio_dev_attr_master_mode_available.dev_attr.attr, NULL, }; static const struct attribute_group stm32_trigger_attr_group = { .attrs = stm32_trigger_attrs, }; static const struct attribute_group *stm32_trigger_attr_groups[] = { &stm32_trigger_attr_group, NULL, }; static const struct iio_trigger_ops timer_trigger_ops = { }; static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) { int ret; const char * const *cur = priv->triggers; while (cur && *cur) { struct iio_trigger *trig; bool cur_is_trgo = stm32_timer_is_trgo_name(*cur); bool cur_is_trgo2 = stm32_timer_is_trgo2_name(*cur); if (cur_is_trgo2 && !priv->has_trgo2) { cur++; continue; } trig = devm_iio_trigger_alloc(priv->dev, "%s", *cur); if (!trig) return -ENOMEM; trig->dev.parent = priv->dev->parent; trig->ops = &timer_trigger_ops; /* * sampling frequency and master mode attributes * should only be available on trgo/trgo2 triggers */ if (cur_is_trgo || cur_is_trgo2) trig->dev.groups = stm32_trigger_attr_groups; iio_trigger_set_drvdata(trig, priv); ret = devm_iio_trigger_register(priv->dev, trig); if (ret) return ret; cur++; } return 0; } static int stm32_counter_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); u32 dat; switch (mask) { case IIO_CHAN_INFO_RAW: regmap_read(priv->regmap, TIM_CNT, &dat); *val = dat; return IIO_VAL_INT; case IIO_CHAN_INFO_ENABLE: regmap_read(priv->regmap, TIM_CR1, &dat); *val = (dat & TIM_CR1_CEN) ? 1 : 0; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: regmap_read(priv->regmap, TIM_SMCR, &dat); dat &= TIM_SMCR_SMS; *val = 1; *val2 = 0; /* in quadrature case scale = 0.25 */ if (dat == 3) *val2 = 2; return IIO_VAL_FRACTIONAL_LOG2; } return -EINVAL; } static int stm32_counter_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); u32 dat; switch (mask) { case IIO_CHAN_INFO_RAW: return regmap_write(priv->regmap, TIM_CNT, val); case IIO_CHAN_INFO_SCALE: /* fixed scale */ return -EINVAL; case IIO_CHAN_INFO_ENABLE: if (val) { regmap_read(priv->regmap, TIM_CR1, &dat); if (!(dat & TIM_CR1_CEN)) clk_enable(priv->clk); regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); } else { regmap_read(priv->regmap, TIM_CR1, &dat); regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); if (dat & TIM_CR1_CEN) clk_disable(priv->clk); } return 0; } return -EINVAL; } static int stm32_counter_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); const char * const *cur = priv->valids; unsigned int i = 0; if (!is_stm32_timer_trigger(trig)) return -EINVAL; while (cur && *cur) { if (!strncmp(trig->name, *cur, strlen(trig->name))) { regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_TS, i << TIM_SMCR_TS_SHIFT); return 0; } cur++; i++; } return -EINVAL; } static const struct iio_info stm32_trigger_info = { .validate_trigger = stm32_counter_validate_trigger, .read_raw = stm32_counter_read_raw, .write_raw = stm32_counter_write_raw }; static const char *const stm32_trigger_modes[] = { "trigger", }; static int stm32_set_trigger_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int mode) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, TIM_SMCR_SMS); return 0; } static int stm32_get_trigger_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); u32 smcr; regmap_read(priv->regmap, TIM_SMCR, &smcr); return (smcr & TIM_SMCR_SMS) == TIM_SMCR_SMS ? 0 : -EINVAL; } static const struct iio_enum stm32_trigger_mode_enum = { .items = stm32_trigger_modes, .num_items = ARRAY_SIZE(stm32_trigger_modes), .set = stm32_set_trigger_mode, .get = stm32_get_trigger_mode }; static const char *const stm32_enable_modes[] = { "always", "gated", "triggered", }; static int stm32_enable_mode2sms(int mode) { switch (mode) { case 0: return 0; case 1: return 5; case 2: return 6; } return -EINVAL; } static int stm32_set_enable_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int mode) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); int sms = stm32_enable_mode2sms(mode); u32 val; if (sms < 0) return sms; /* * Triggered mode sets CEN bit automatically by hardware. So, first * enable counter clock, so it can use it. Keeps it in sync with CEN. */ if (sms == 6) { regmap_read(priv->regmap, TIM_CR1, &val); if (!(val & TIM_CR1_CEN)) clk_enable(priv->clk); } regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms); return 0; } static int stm32_sms2enable_mode(int mode) { switch (mode) { case 0: return 0; case 5: return 1; case 6: return 2; } return -EINVAL; } static int stm32_get_enable_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); u32 smcr; regmap_read(priv->regmap, TIM_SMCR, &smcr); smcr &= TIM_SMCR_SMS; return stm32_sms2enable_mode(smcr); } static const struct iio_enum stm32_enable_mode_enum = { .items = stm32_enable_modes, .num_items = ARRAY_SIZE(stm32_enable_modes), .set = stm32_set_enable_mode, .get = stm32_get_enable_mode }; static ssize_t stm32_count_get_preset(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); u32 arr; regmap_read(priv->regmap, TIM_ARR, &arr); return snprintf(buf, PAGE_SIZE, "%u\n", arr); } static ssize_t stm32_count_set_preset(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, const char *buf, size_t len) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); unsigned int preset; int ret; ret = kstrtouint(buf, 0, &preset); if (ret) return ret; /* TIMx_ARR register shouldn't be buffered (ARPE=0) */ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0); regmap_write(priv->regmap, TIM_ARR, preset); return len; } static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = { { .name = "preset", .shared = IIO_SEPARATE, .read = stm32_count_get_preset, .write = stm32_count_set_preset }, IIO_ENUM("enable_mode", IIO_SEPARATE, &stm32_enable_mode_enum), IIO_ENUM_AVAILABLE("enable_mode", &stm32_enable_mode_enum), IIO_ENUM("trigger_mode", IIO_SEPARATE, &stm32_trigger_mode_enum), IIO_ENUM_AVAILABLE("trigger_mode", &stm32_trigger_mode_enum), {} }; static const struct iio_chan_spec stm32_trigger_channel = { .type = IIO_COUNT, .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), .ext_info = stm32_trigger_count_info, .indexed = 1 }; static struct stm32_timer_trigger *stm32_setup_counter_device(struct device *dev) { struct iio_dev *indio_dev; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(struct stm32_timer_trigger)); if (!indio_dev) return NULL; indio_dev->name = dev_name(dev); indio_dev->dev.parent = dev; indio_dev->info = &stm32_trigger_info; indio_dev->modes = INDIO_HARDWARE_TRIGGERED; indio_dev->num_channels = 1; indio_dev->channels = &stm32_trigger_channel; indio_dev->dev.of_node = dev->of_node; ret = devm_iio_device_register(dev, indio_dev); if (ret) return NULL; return iio_priv(indio_dev); } /** * is_stm32_timer_trigger * @trig: trigger to be checked * * return true if the trigger is a valid stm32 iio timer trigger * either return false */ bool is_stm32_timer_trigger(struct iio_trigger *trig) { return (trig->ops == &timer_trigger_ops); } EXPORT_SYMBOL(is_stm32_timer_trigger); static void stm32_timer_detect_trgo2(struct stm32_timer_trigger *priv) { u32 val; /* * Master mode selection 2 bits can only be written and read back when * timer supports it. */ regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2, TIM_CR2_MMS2); regmap_read(priv->regmap, TIM_CR2, &val); regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2, 0); priv->has_trgo2 = !!val; } static int stm32_timer_trigger_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct stm32_timer_trigger *priv; struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent); const struct stm32_timer_trigger_cfg *cfg; unsigned int index; int ret; if (of_property_read_u32(dev->of_node, "reg", &index)) return -EINVAL; cfg = (const struct stm32_timer_trigger_cfg *) of_match_device(dev->driver->of_match_table, dev)->data; if (index >= ARRAY_SIZE(triggers_table) || index >= cfg->num_valids_table) return -EINVAL; /* Create an IIO device only if we have triggers to be validated */ if (*cfg->valids_table[index]) priv = stm32_setup_counter_device(dev); else priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = dev; priv->regmap = ddata->regmap; priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; priv->triggers = triggers_table[index]; priv->valids = cfg->valids_table[index]; stm32_timer_detect_trgo2(priv); ret = stm32_setup_iio_triggers(priv); if (ret) return ret; platform_set_drvdata(pdev, priv); return 0; } static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = { .valids_table = valids_table, .num_valids_table = ARRAY_SIZE(valids_table), }; static const struct stm32_timer_trigger_cfg stm32h7_timer_trg_cfg = { .valids_table = stm32h7_valids_table, .num_valids_table = ARRAY_SIZE(stm32h7_valids_table), }; static const struct of_device_id stm32_trig_of_match[] = { { .compatible = "st,stm32-timer-trigger", .data = (void *)&stm32_timer_trg_cfg, }, { .compatible = "st,stm32h7-timer-trigger", .data = (void *)&stm32h7_timer_trg_cfg, }, { /* end node */ }, }; MODULE_DEVICE_TABLE(of, stm32_trig_of_match); static struct platform_driver stm32_timer_trigger_driver = { .probe = stm32_timer_trigger_probe, .driver = { .name = "stm32-timer-trigger", .of_match_table = stm32_trig_of_match, }, }; module_platform_driver(stm32_timer_trigger_driver); MODULE_ALIAS("platform: stm32-timer-trigger"); MODULE_DESCRIPTION("STMicroelectronics STM32 Timer Trigger driver"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1