root/drivers/counter/ftm-quaddec.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ftm_read
  2. ftm_write
  3. ftm_clear_write_protection
  4. ftm_set_write_protection
  5. ftm_reset_counter
  6. ftm_quaddec_init
  7. ftm_quaddec_disable
  8. ftm_quaddec_get_prescaler
  9. ftm_quaddec_set_prescaler
  10. ftm_quaddec_count_read
  11. ftm_quaddec_count_write
  12. ftm_quaddec_count_function_get
  13. ftm_quaddec_action_get
  14. ftm_quaddec_probe

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Flex Timer Module Quadrature decoder
   4  *
   5  * This module implements a driver for decoding the FTM quadrature
   6  * of ex. a LS1021A
   7  */
   8 
   9 #include <linux/fsl/ftm.h>
  10 #include <linux/module.h>
  11 #include <linux/platform_device.h>
  12 #include <linux/of.h>
  13 #include <linux/io.h>
  14 #include <linux/mutex.h>
  15 #include <linux/counter.h>
  16 #include <linux/bitfield.h>
  17 
  18 #define FTM_FIELD_UPDATE(ftm, offset, mask, val)                        \
  19         ({                                                              \
  20                 uint32_t flags;                                         \
  21                 ftm_read(ftm, offset, &flags);                          \
  22                 flags &= ~mask;                                         \
  23                 flags |= FIELD_PREP(mask, val);                         \
  24                 ftm_write(ftm, offset, flags);                          \
  25         })
  26 
  27 struct ftm_quaddec {
  28         struct counter_device counter;
  29         struct platform_device *pdev;
  30         void __iomem *ftm_base;
  31         bool big_endian;
  32         struct mutex ftm_quaddec_mutex;
  33 };
  34 
  35 static void ftm_read(struct ftm_quaddec *ftm, uint32_t offset, uint32_t *data)
  36 {
  37         if (ftm->big_endian)
  38                 *data = ioread32be(ftm->ftm_base + offset);
  39         else
  40                 *data = ioread32(ftm->ftm_base + offset);
  41 }
  42 
  43 static void ftm_write(struct ftm_quaddec *ftm, uint32_t offset, uint32_t data)
  44 {
  45         if (ftm->big_endian)
  46                 iowrite32be(data, ftm->ftm_base + offset);
  47         else
  48                 iowrite32(data, ftm->ftm_base + offset);
  49 }
  50 
  51 /* Hold mutex before modifying write protection state */
  52 static void ftm_clear_write_protection(struct ftm_quaddec *ftm)
  53 {
  54         uint32_t flag;
  55 
  56         /* First see if it is enabled */
  57         ftm_read(ftm, FTM_FMS, &flag);
  58 
  59         if (flag & FTM_FMS_WPEN)
  60                 FTM_FIELD_UPDATE(ftm, FTM_MODE, FTM_MODE_WPDIS, 1);
  61 }
  62 
  63 static void ftm_set_write_protection(struct ftm_quaddec *ftm)
  64 {
  65         FTM_FIELD_UPDATE(ftm, FTM_FMS, FTM_FMS_WPEN, 1);
  66 }
  67 
  68 static void ftm_reset_counter(struct ftm_quaddec *ftm)
  69 {
  70         /* Reset hardware counter to CNTIN */
  71         ftm_write(ftm, FTM_CNT, 0x0);
  72 }
  73 
  74 static void ftm_quaddec_init(struct ftm_quaddec *ftm)
  75 {
  76         ftm_clear_write_protection(ftm);
  77 
  78         /*
  79          * Do not write in the region from the CNTIN register through the
  80          * PWMLOAD register when FTMEN = 0.
  81          * Also reset other fields to zero
  82          */
  83         ftm_write(ftm, FTM_MODE, FTM_MODE_FTMEN);
  84         ftm_write(ftm, FTM_CNTIN, 0x0000);
  85         ftm_write(ftm, FTM_MOD, 0xffff);
  86         ftm_write(ftm, FTM_CNT, 0x0);
  87         /* Set prescaler, reset other fields to zero */
  88         ftm_write(ftm, FTM_SC, FTM_SC_PS_1);
  89 
  90         /* Select quad mode, reset other fields to zero */
  91         ftm_write(ftm, FTM_QDCTRL, FTM_QDCTRL_QUADEN);
  92 
  93         /* Unused features and reset to default section */
  94         ftm_write(ftm, FTM_POL, 0x0);
  95         ftm_write(ftm, FTM_FLTCTRL, 0x0);
  96         ftm_write(ftm, FTM_SYNCONF, 0x0);
  97         ftm_write(ftm, FTM_SYNC, 0xffff);
  98 
  99         /* Lock the FTM */
 100         ftm_set_write_protection(ftm);
 101 }
 102 
 103 static void ftm_quaddec_disable(void *ftm)
 104 {
 105         struct ftm_quaddec *ftm_qua = ftm;
 106 
 107         ftm_clear_write_protection(ftm_qua);
 108         ftm_write(ftm_qua, FTM_MODE, 0);
 109         ftm_write(ftm_qua, FTM_QDCTRL, 0);
 110         /*
 111          * This is enough to disable the counter. No clock has been
 112          * selected by writing to FTM_SC in init()
 113          */
 114         ftm_set_write_protection(ftm_qua);
 115 }
 116 
 117 static int ftm_quaddec_get_prescaler(struct counter_device *counter,
 118                                      struct counter_count *count,
 119                                      size_t *cnt_mode)
 120 {
 121         struct ftm_quaddec *ftm = counter->priv;
 122         uint32_t scflags;
 123 
 124         ftm_read(ftm, FTM_SC, &scflags);
 125 
 126         *cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags);
 127 
 128         return 0;
 129 }
 130 
 131 static int ftm_quaddec_set_prescaler(struct counter_device *counter,
 132                                      struct counter_count *count,
 133                                      size_t cnt_mode)
 134 {
 135         struct ftm_quaddec *ftm = counter->priv;
 136 
 137         mutex_lock(&ftm->ftm_quaddec_mutex);
 138 
 139         ftm_clear_write_protection(ftm);
 140         FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode);
 141         ftm_set_write_protection(ftm);
 142 
 143         /* Also resets the counter as it is undefined anyway now */
 144         ftm_reset_counter(ftm);
 145 
 146         mutex_unlock(&ftm->ftm_quaddec_mutex);
 147         return 0;
 148 }
 149 
 150 static const char * const ftm_quaddec_prescaler[] = {
 151         "1", "2", "4", "8", "16", "32", "64", "128"
 152 };
 153 
 154 static struct counter_count_enum_ext ftm_quaddec_prescaler_enum = {
 155         .items = ftm_quaddec_prescaler,
 156         .num_items = ARRAY_SIZE(ftm_quaddec_prescaler),
 157         .get = ftm_quaddec_get_prescaler,
 158         .set = ftm_quaddec_set_prescaler
 159 };
 160 
 161 enum ftm_quaddec_synapse_action {
 162         FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES,
 163 };
 164 
 165 static enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
 166         [FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES] =
 167         COUNTER_SYNAPSE_ACTION_BOTH_EDGES
 168 };
 169 
 170 enum ftm_quaddec_count_function {
 171         FTM_QUADDEC_COUNT_ENCODER_MODE_1,
 172 };
 173 
 174 static const enum counter_count_function ftm_quaddec_count_functions[] = {
 175         [FTM_QUADDEC_COUNT_ENCODER_MODE_1] =
 176         COUNTER_COUNT_FUNCTION_QUADRATURE_X4
 177 };
 178 
 179 static int ftm_quaddec_count_read(struct counter_device *counter,
 180                                   struct counter_count *count,
 181                                   struct counter_count_read_value *val)
 182 {
 183         struct ftm_quaddec *const ftm = counter->priv;
 184         uint32_t cntval;
 185 
 186         ftm_read(ftm, FTM_CNT, &cntval);
 187 
 188         counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cntval);
 189 
 190         return 0;
 191 }
 192 
 193 static int ftm_quaddec_count_write(struct counter_device *counter,
 194                                    struct counter_count *count,
 195                                    struct counter_count_write_value *val)
 196 {
 197         struct ftm_quaddec *const ftm = counter->priv;
 198         u32 cnt;
 199         int err;
 200 
 201         err = counter_count_write_value_get(&cnt, COUNTER_COUNT_POSITION, val);
 202         if (err)
 203                 return err;
 204 
 205         if (cnt != 0) {
 206                 dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
 207                 return -EINVAL;
 208         }
 209 
 210         ftm_reset_counter(ftm);
 211 
 212         return 0;
 213 }
 214 
 215 static int ftm_quaddec_count_function_get(struct counter_device *counter,
 216                                           struct counter_count *count,
 217                                           size_t *function)
 218 {
 219         *function = FTM_QUADDEC_COUNT_ENCODER_MODE_1;
 220 
 221         return 0;
 222 }
 223 
 224 static int ftm_quaddec_action_get(struct counter_device *counter,
 225                                   struct counter_count *count,
 226                                   struct counter_synapse *synapse,
 227                                   size_t *action)
 228 {
 229         *action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES;
 230 
 231         return 0;
 232 }
 233 
 234 static const struct counter_ops ftm_quaddec_cnt_ops = {
 235         .count_read = ftm_quaddec_count_read,
 236         .count_write = ftm_quaddec_count_write,
 237         .function_get = ftm_quaddec_count_function_get,
 238         .action_get = ftm_quaddec_action_get,
 239 };
 240 
 241 static struct counter_signal ftm_quaddec_signals[] = {
 242         {
 243                 .id = 0,
 244                 .name = "Channel 1 Phase A"
 245         },
 246         {
 247                 .id = 1,
 248                 .name = "Channel 1 Phase B"
 249         }
 250 };
 251 
 252 static struct counter_synapse ftm_quaddec_count_synapses[] = {
 253         {
 254                 .actions_list = ftm_quaddec_synapse_actions,
 255                 .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
 256                 .signal = &ftm_quaddec_signals[0]
 257         },
 258         {
 259                 .actions_list = ftm_quaddec_synapse_actions,
 260                 .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
 261                 .signal = &ftm_quaddec_signals[1]
 262         }
 263 };
 264 
 265 static const struct counter_count_ext ftm_quaddec_count_ext[] = {
 266         COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum),
 267         COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum),
 268 };
 269 
 270 static struct counter_count ftm_quaddec_counts = {
 271         .id = 0,
 272         .name = "Channel 1 Count",
 273         .functions_list = ftm_quaddec_count_functions,
 274         .num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),
 275         .synapses = ftm_quaddec_count_synapses,
 276         .num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),
 277         .ext = ftm_quaddec_count_ext,
 278         .num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)
 279 };
 280 
 281 static int ftm_quaddec_probe(struct platform_device *pdev)
 282 {
 283         struct ftm_quaddec *ftm;
 284 
 285         struct device_node *node = pdev->dev.of_node;
 286         struct resource *io;
 287         int ret;
 288 
 289         ftm = devm_kzalloc(&pdev->dev, sizeof(*ftm), GFP_KERNEL);
 290         if (!ftm)
 291                 return -ENOMEM;
 292 
 293         platform_set_drvdata(pdev, ftm);
 294 
 295         io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 296         if (!io) {
 297                 dev_err(&pdev->dev, "Failed to get memory region\n");
 298                 return -ENODEV;
 299         }
 300 
 301         ftm->pdev = pdev;
 302         ftm->big_endian = of_property_read_bool(node, "big-endian");
 303         ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));
 304 
 305         if (!ftm->ftm_base) {
 306                 dev_err(&pdev->dev, "Failed to map memory region\n");
 307                 return -EINVAL;
 308         }
 309         ftm->counter.name = dev_name(&pdev->dev);
 310         ftm->counter.parent = &pdev->dev;
 311         ftm->counter.ops = &ftm_quaddec_cnt_ops;
 312         ftm->counter.counts = &ftm_quaddec_counts;
 313         ftm->counter.num_counts = 1;
 314         ftm->counter.signals = ftm_quaddec_signals;
 315         ftm->counter.num_signals = ARRAY_SIZE(ftm_quaddec_signals);
 316         ftm->counter.priv = ftm;
 317 
 318         mutex_init(&ftm->ftm_quaddec_mutex);
 319 
 320         ftm_quaddec_init(ftm);
 321 
 322         ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
 323         if (ret)
 324                 return ret;
 325 
 326         ret = devm_counter_register(&pdev->dev, &ftm->counter);
 327         if (ret)
 328                 return ret;
 329 
 330         return 0;
 331 }
 332 
 333 static const struct of_device_id ftm_quaddec_match[] = {
 334         { .compatible = "fsl,ftm-quaddec" },
 335         {},
 336 };
 337 
 338 static struct platform_driver ftm_quaddec_driver = {
 339         .driver = {
 340                 .name = "ftm-quaddec",
 341                 .of_match_table = ftm_quaddec_match,
 342         },
 343         .probe = ftm_quaddec_probe,
 344 };
 345 
 346 module_platform_driver(ftm_quaddec_driver);
 347 
 348 MODULE_LICENSE("GPL");
 349 MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
 350 MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");

/* [<][>][^][v][top][bottom][index][help] */