Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Flex Timer Module Quadrature decoder
0004  *
0005  * This module implements a driver for decoding the FTM quadrature
0006  * of ex. a LS1021A
0007  */
0008 
0009 #include <linux/fsl/ftm.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/of.h>
0013 #include <linux/io.h>
0014 #include <linux/mutex.h>
0015 #include <linux/counter.h>
0016 #include <linux/bitfield.h>
0017 #include <linux/types.h>
0018 
0019 #define FTM_FIELD_UPDATE(ftm, offset, mask, val)            \
0020     ({                              \
0021         uint32_t flags;                     \
0022         ftm_read(ftm, offset, &flags);              \
0023         flags &= ~mask;                     \
0024         flags |= FIELD_PREP(mask, val);             \
0025         ftm_write(ftm, offset, flags);              \
0026     })
0027 
0028 struct ftm_quaddec {
0029     struct platform_device *pdev;
0030     void __iomem *ftm_base;
0031     bool big_endian;
0032     struct mutex ftm_quaddec_mutex;
0033 };
0034 
0035 static void ftm_read(struct ftm_quaddec *ftm, uint32_t offset, uint32_t *data)
0036 {
0037     if (ftm->big_endian)
0038         *data = ioread32be(ftm->ftm_base + offset);
0039     else
0040         *data = ioread32(ftm->ftm_base + offset);
0041 }
0042 
0043 static void ftm_write(struct ftm_quaddec *ftm, uint32_t offset, uint32_t data)
0044 {
0045     if (ftm->big_endian)
0046         iowrite32be(data, ftm->ftm_base + offset);
0047     else
0048         iowrite32(data, ftm->ftm_base + offset);
0049 }
0050 
0051 /* Hold mutex before modifying write protection state */
0052 static void ftm_clear_write_protection(struct ftm_quaddec *ftm)
0053 {
0054     uint32_t flag;
0055 
0056     /* First see if it is enabled */
0057     ftm_read(ftm, FTM_FMS, &flag);
0058 
0059     if (flag & FTM_FMS_WPEN)
0060         FTM_FIELD_UPDATE(ftm, FTM_MODE, FTM_MODE_WPDIS, 1);
0061 }
0062 
0063 static void ftm_set_write_protection(struct ftm_quaddec *ftm)
0064 {
0065     FTM_FIELD_UPDATE(ftm, FTM_FMS, FTM_FMS_WPEN, 1);
0066 }
0067 
0068 static void ftm_reset_counter(struct ftm_quaddec *ftm)
0069 {
0070     /* Reset hardware counter to CNTIN */
0071     ftm_write(ftm, FTM_CNT, 0x0);
0072 }
0073 
0074 static void ftm_quaddec_init(struct ftm_quaddec *ftm)
0075 {
0076     ftm_clear_write_protection(ftm);
0077 
0078     /*
0079      * Do not write in the region from the CNTIN register through the
0080      * PWMLOAD register when FTMEN = 0.
0081      * Also reset other fields to zero
0082      */
0083     ftm_write(ftm, FTM_MODE, FTM_MODE_FTMEN);
0084     ftm_write(ftm, FTM_CNTIN, 0x0000);
0085     ftm_write(ftm, FTM_MOD, 0xffff);
0086     ftm_write(ftm, FTM_CNT, 0x0);
0087     /* Set prescaler, reset other fields to zero */
0088     ftm_write(ftm, FTM_SC, FTM_SC_PS_1);
0089 
0090     /* Select quad mode, reset other fields to zero */
0091     ftm_write(ftm, FTM_QDCTRL, FTM_QDCTRL_QUADEN);
0092 
0093     /* Unused features and reset to default section */
0094     ftm_write(ftm, FTM_POL, 0x0);
0095     ftm_write(ftm, FTM_FLTCTRL, 0x0);
0096     ftm_write(ftm, FTM_SYNCONF, 0x0);
0097     ftm_write(ftm, FTM_SYNC, 0xffff);
0098 
0099     /* Lock the FTM */
0100     ftm_set_write_protection(ftm);
0101 }
0102 
0103 static void ftm_quaddec_disable(void *ftm)
0104 {
0105     struct ftm_quaddec *ftm_qua = ftm;
0106 
0107     ftm_clear_write_protection(ftm_qua);
0108     ftm_write(ftm_qua, FTM_MODE, 0);
0109     ftm_write(ftm_qua, FTM_QDCTRL, 0);
0110     /*
0111      * This is enough to disable the counter. No clock has been
0112      * selected by writing to FTM_SC in init()
0113      */
0114     ftm_set_write_protection(ftm_qua);
0115 }
0116 
0117 static int ftm_quaddec_get_prescaler(struct counter_device *counter,
0118                      struct counter_count *count, u32 *cnt_mode)
0119 {
0120     struct ftm_quaddec *ftm = counter_priv(counter);
0121     uint32_t scflags;
0122 
0123     ftm_read(ftm, FTM_SC, &scflags);
0124 
0125     *cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags);
0126 
0127     return 0;
0128 }
0129 
0130 static int ftm_quaddec_set_prescaler(struct counter_device *counter,
0131                      struct counter_count *count, u32 cnt_mode)
0132 {
0133     struct ftm_quaddec *ftm = counter_priv(counter);
0134 
0135     mutex_lock(&ftm->ftm_quaddec_mutex);
0136 
0137     ftm_clear_write_protection(ftm);
0138     FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode);
0139     ftm_set_write_protection(ftm);
0140 
0141     /* Also resets the counter as it is undefined anyway now */
0142     ftm_reset_counter(ftm);
0143 
0144     mutex_unlock(&ftm->ftm_quaddec_mutex);
0145     return 0;
0146 }
0147 
0148 static const char * const ftm_quaddec_prescaler[] = {
0149     "1", "2", "4", "8", "16", "32", "64", "128"
0150 };
0151 
0152 static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
0153     COUNTER_SYNAPSE_ACTION_BOTH_EDGES
0154 };
0155 
0156 static const enum counter_function ftm_quaddec_count_functions[] = {
0157     COUNTER_FUNCTION_QUADRATURE_X4
0158 };
0159 
0160 static int ftm_quaddec_count_read(struct counter_device *counter,
0161                   struct counter_count *count,
0162                   u64 *val)
0163 {
0164     struct ftm_quaddec *const ftm = counter_priv(counter);
0165     uint32_t cntval;
0166 
0167     ftm_read(ftm, FTM_CNT, &cntval);
0168 
0169     *val = cntval;
0170 
0171     return 0;
0172 }
0173 
0174 static int ftm_quaddec_count_write(struct counter_device *counter,
0175                    struct counter_count *count,
0176                    const u64 val)
0177 {
0178     struct ftm_quaddec *const ftm = counter_priv(counter);
0179 
0180     if (val != 0) {
0181         dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
0182         return -EINVAL;
0183     }
0184 
0185     ftm_reset_counter(ftm);
0186 
0187     return 0;
0188 }
0189 
0190 static int ftm_quaddec_count_function_read(struct counter_device *counter,
0191                        struct counter_count *count,
0192                        enum counter_function *function)
0193 {
0194     *function = COUNTER_FUNCTION_QUADRATURE_X4;
0195 
0196     return 0;
0197 }
0198 
0199 static int ftm_quaddec_action_read(struct counter_device *counter,
0200                    struct counter_count *count,
0201                    struct counter_synapse *synapse,
0202                    enum counter_synapse_action *action)
0203 {
0204     *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
0205 
0206     return 0;
0207 }
0208 
0209 static const struct counter_ops ftm_quaddec_cnt_ops = {
0210     .count_read = ftm_quaddec_count_read,
0211     .count_write = ftm_quaddec_count_write,
0212     .function_read = ftm_quaddec_count_function_read,
0213     .action_read = ftm_quaddec_action_read,
0214 };
0215 
0216 static struct counter_signal ftm_quaddec_signals[] = {
0217     {
0218         .id = 0,
0219         .name = "Channel 1 Phase A"
0220     },
0221     {
0222         .id = 1,
0223         .name = "Channel 1 Phase B"
0224     }
0225 };
0226 
0227 static struct counter_synapse ftm_quaddec_count_synapses[] = {
0228     {
0229         .actions_list = ftm_quaddec_synapse_actions,
0230         .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
0231         .signal = &ftm_quaddec_signals[0]
0232     },
0233     {
0234         .actions_list = ftm_quaddec_synapse_actions,
0235         .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
0236         .signal = &ftm_quaddec_signals[1]
0237     }
0238 };
0239 
0240 static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler);
0241 
0242 static struct counter_comp ftm_quaddec_count_ext[] = {
0243     COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler,
0244                 ftm_quaddec_set_prescaler,
0245                 ftm_quaddec_prescaler_enum),
0246 };
0247 
0248 static struct counter_count ftm_quaddec_counts = {
0249     .id = 0,
0250     .name = "Channel 1 Count",
0251     .functions_list = ftm_quaddec_count_functions,
0252     .num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),
0253     .synapses = ftm_quaddec_count_synapses,
0254     .num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),
0255     .ext = ftm_quaddec_count_ext,
0256     .num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)
0257 };
0258 
0259 static int ftm_quaddec_probe(struct platform_device *pdev)
0260 {
0261     struct counter_device *counter;
0262     struct ftm_quaddec *ftm;
0263 
0264     struct device_node *node = pdev->dev.of_node;
0265     struct resource *io;
0266     int ret;
0267 
0268     counter = devm_counter_alloc(&pdev->dev, sizeof(*ftm));
0269     if (!counter)
0270         return -ENOMEM;
0271     ftm = counter_priv(counter);
0272 
0273     io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0274     if (!io) {
0275         dev_err(&pdev->dev, "Failed to get memory region\n");
0276         return -ENODEV;
0277     }
0278 
0279     ftm->pdev = pdev;
0280     ftm->big_endian = of_property_read_bool(node, "big-endian");
0281     ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));
0282 
0283     if (!ftm->ftm_base) {
0284         dev_err(&pdev->dev, "Failed to map memory region\n");
0285         return -EINVAL;
0286     }
0287     counter->name = dev_name(&pdev->dev);
0288     counter->parent = &pdev->dev;
0289     counter->ops = &ftm_quaddec_cnt_ops;
0290     counter->counts = &ftm_quaddec_counts;
0291     counter->num_counts = 1;
0292     counter->signals = ftm_quaddec_signals;
0293     counter->num_signals = ARRAY_SIZE(ftm_quaddec_signals);
0294 
0295     mutex_init(&ftm->ftm_quaddec_mutex);
0296 
0297     ftm_quaddec_init(ftm);
0298 
0299     ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
0300     if (ret)
0301         return ret;
0302 
0303     ret = devm_counter_add(&pdev->dev, counter);
0304     if (ret)
0305         return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");
0306 
0307     return 0;
0308 }
0309 
0310 static const struct of_device_id ftm_quaddec_match[] = {
0311     { .compatible = "fsl,ftm-quaddec" },
0312     {},
0313 };
0314 
0315 static struct platform_driver ftm_quaddec_driver = {
0316     .driver = {
0317         .name = "ftm-quaddec",
0318         .of_match_table = ftm_quaddec_match,
0319     },
0320     .probe = ftm_quaddec_probe,
0321 };
0322 
0323 module_platform_driver(ftm_quaddec_driver);
0324 
0325 MODULE_LICENSE("GPL");
0326 MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
0327 MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");