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