18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Flex Timer Module Quadrature decoder 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This module implements a driver for decoding the FTM quadrature 68c2ecf20Sopenharmony_ci * of ex. a LS1021A 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/fsl/ftm.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/counter.h> 168c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define FTM_FIELD_UPDATE(ftm, offset, mask, val) \ 198c2ecf20Sopenharmony_ci ({ \ 208c2ecf20Sopenharmony_ci uint32_t flags; \ 218c2ecf20Sopenharmony_ci ftm_read(ftm, offset, &flags); \ 228c2ecf20Sopenharmony_ci flags &= ~mask; \ 238c2ecf20Sopenharmony_ci flags |= FIELD_PREP(mask, val); \ 248c2ecf20Sopenharmony_ci ftm_write(ftm, offset, flags); \ 258c2ecf20Sopenharmony_ci }) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct ftm_quaddec { 288c2ecf20Sopenharmony_ci struct counter_device counter; 298c2ecf20Sopenharmony_ci struct platform_device *pdev; 308c2ecf20Sopenharmony_ci void __iomem *ftm_base; 318c2ecf20Sopenharmony_ci bool big_endian; 328c2ecf20Sopenharmony_ci struct mutex ftm_quaddec_mutex; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void ftm_read(struct ftm_quaddec *ftm, uint32_t offset, uint32_t *data) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci if (ftm->big_endian) 388c2ecf20Sopenharmony_ci *data = ioread32be(ftm->ftm_base + offset); 398c2ecf20Sopenharmony_ci else 408c2ecf20Sopenharmony_ci *data = ioread32(ftm->ftm_base + offset); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void ftm_write(struct ftm_quaddec *ftm, uint32_t offset, uint32_t data) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci if (ftm->big_endian) 468c2ecf20Sopenharmony_ci iowrite32be(data, ftm->ftm_base + offset); 478c2ecf20Sopenharmony_ci else 488c2ecf20Sopenharmony_ci iowrite32(data, ftm->ftm_base + offset); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Hold mutex before modifying write protection state */ 528c2ecf20Sopenharmony_cistatic void ftm_clear_write_protection(struct ftm_quaddec *ftm) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci uint32_t flag; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* First see if it is enabled */ 578c2ecf20Sopenharmony_ci ftm_read(ftm, FTM_FMS, &flag); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (flag & FTM_FMS_WPEN) 608c2ecf20Sopenharmony_ci FTM_FIELD_UPDATE(ftm, FTM_MODE, FTM_MODE_WPDIS, 1); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void ftm_set_write_protection(struct ftm_quaddec *ftm) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci FTM_FIELD_UPDATE(ftm, FTM_FMS, FTM_FMS_WPEN, 1); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void ftm_reset_counter(struct ftm_quaddec *ftm) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci /* Reset hardware counter to CNTIN */ 718c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_CNT, 0x0); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void ftm_quaddec_init(struct ftm_quaddec *ftm) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci ftm_clear_write_protection(ftm); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * Do not write in the region from the CNTIN register through the 808c2ecf20Sopenharmony_ci * PWMLOAD register when FTMEN = 0. 818c2ecf20Sopenharmony_ci * Also reset other fields to zero 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_MODE, FTM_MODE_FTMEN); 848c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_CNTIN, 0x0000); 858c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_MOD, 0xffff); 868c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_CNT, 0x0); 878c2ecf20Sopenharmony_ci /* Set prescaler, reset other fields to zero */ 888c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_SC, FTM_SC_PS_1); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Select quad mode, reset other fields to zero */ 918c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_QDCTRL, FTM_QDCTRL_QUADEN); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Unused features and reset to default section */ 948c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_POL, 0x0); 958c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_FLTCTRL, 0x0); 968c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_SYNCONF, 0x0); 978c2ecf20Sopenharmony_ci ftm_write(ftm, FTM_SYNC, 0xffff); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Lock the FTM */ 1008c2ecf20Sopenharmony_ci ftm_set_write_protection(ftm); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void ftm_quaddec_disable(void *ftm) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct ftm_quaddec *ftm_qua = ftm; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ftm_clear_write_protection(ftm_qua); 1088c2ecf20Sopenharmony_ci ftm_write(ftm_qua, FTM_MODE, 0); 1098c2ecf20Sopenharmony_ci ftm_write(ftm_qua, FTM_QDCTRL, 0); 1108c2ecf20Sopenharmony_ci /* 1118c2ecf20Sopenharmony_ci * This is enough to disable the counter. No clock has been 1128c2ecf20Sopenharmony_ci * selected by writing to FTM_SC in init() 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci ftm_set_write_protection(ftm_qua); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int ftm_quaddec_get_prescaler(struct counter_device *counter, 1188c2ecf20Sopenharmony_ci struct counter_count *count, 1198c2ecf20Sopenharmony_ci size_t *cnt_mode) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct ftm_quaddec *ftm = counter->priv; 1228c2ecf20Sopenharmony_ci uint32_t scflags; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci ftm_read(ftm, FTM_SC, &scflags); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci *cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int ftm_quaddec_set_prescaler(struct counter_device *counter, 1328c2ecf20Sopenharmony_ci struct counter_count *count, 1338c2ecf20Sopenharmony_ci size_t cnt_mode) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct ftm_quaddec *ftm = counter->priv; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci mutex_lock(&ftm->ftm_quaddec_mutex); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci ftm_clear_write_protection(ftm); 1408c2ecf20Sopenharmony_ci FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode); 1418c2ecf20Sopenharmony_ci ftm_set_write_protection(ftm); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Also resets the counter as it is undefined anyway now */ 1448c2ecf20Sopenharmony_ci ftm_reset_counter(ftm); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci mutex_unlock(&ftm->ftm_quaddec_mutex); 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const char * const ftm_quaddec_prescaler[] = { 1518c2ecf20Sopenharmony_ci "1", "2", "4", "8", "16", "32", "64", "128" 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic struct counter_count_enum_ext ftm_quaddec_prescaler_enum = { 1558c2ecf20Sopenharmony_ci .items = ftm_quaddec_prescaler, 1568c2ecf20Sopenharmony_ci .num_items = ARRAY_SIZE(ftm_quaddec_prescaler), 1578c2ecf20Sopenharmony_ci .get = ftm_quaddec_get_prescaler, 1588c2ecf20Sopenharmony_ci .set = ftm_quaddec_set_prescaler 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cienum ftm_quaddec_synapse_action { 1628c2ecf20Sopenharmony_ci FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES, 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic enum counter_synapse_action ftm_quaddec_synapse_actions[] = { 1668c2ecf20Sopenharmony_ci [FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES] = 1678c2ecf20Sopenharmony_ci COUNTER_SYNAPSE_ACTION_BOTH_EDGES 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cienum ftm_quaddec_count_function { 1718c2ecf20Sopenharmony_ci FTM_QUADDEC_COUNT_ENCODER_MODE_1, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const enum counter_count_function ftm_quaddec_count_functions[] = { 1758c2ecf20Sopenharmony_ci [FTM_QUADDEC_COUNT_ENCODER_MODE_1] = 1768c2ecf20Sopenharmony_ci COUNTER_COUNT_FUNCTION_QUADRATURE_X4 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int ftm_quaddec_count_read(struct counter_device *counter, 1808c2ecf20Sopenharmony_ci struct counter_count *count, 1818c2ecf20Sopenharmony_ci unsigned long *val) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct ftm_quaddec *const ftm = counter->priv; 1848c2ecf20Sopenharmony_ci uint32_t cntval; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ftm_read(ftm, FTM_CNT, &cntval); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci *val = cntval; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int ftm_quaddec_count_write(struct counter_device *counter, 1948c2ecf20Sopenharmony_ci struct counter_count *count, 1958c2ecf20Sopenharmony_ci const unsigned long val) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct ftm_quaddec *const ftm = counter->priv; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (val != 0) { 2008c2ecf20Sopenharmony_ci dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n"); 2018c2ecf20Sopenharmony_ci return -EINVAL; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci ftm_reset_counter(ftm); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int ftm_quaddec_count_function_get(struct counter_device *counter, 2108c2ecf20Sopenharmony_ci struct counter_count *count, 2118c2ecf20Sopenharmony_ci size_t *function) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci *function = FTM_QUADDEC_COUNT_ENCODER_MODE_1; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int ftm_quaddec_action_get(struct counter_device *counter, 2198c2ecf20Sopenharmony_ci struct counter_count *count, 2208c2ecf20Sopenharmony_ci struct counter_synapse *synapse, 2218c2ecf20Sopenharmony_ci size_t *action) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci *action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic const struct counter_ops ftm_quaddec_cnt_ops = { 2298c2ecf20Sopenharmony_ci .count_read = ftm_quaddec_count_read, 2308c2ecf20Sopenharmony_ci .count_write = ftm_quaddec_count_write, 2318c2ecf20Sopenharmony_ci .function_get = ftm_quaddec_count_function_get, 2328c2ecf20Sopenharmony_ci .action_get = ftm_quaddec_action_get, 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic struct counter_signal ftm_quaddec_signals[] = { 2368c2ecf20Sopenharmony_ci { 2378c2ecf20Sopenharmony_ci .id = 0, 2388c2ecf20Sopenharmony_ci .name = "Channel 1 Phase A" 2398c2ecf20Sopenharmony_ci }, 2408c2ecf20Sopenharmony_ci { 2418c2ecf20Sopenharmony_ci .id = 1, 2428c2ecf20Sopenharmony_ci .name = "Channel 1 Phase B" 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic struct counter_synapse ftm_quaddec_count_synapses[] = { 2478c2ecf20Sopenharmony_ci { 2488c2ecf20Sopenharmony_ci .actions_list = ftm_quaddec_synapse_actions, 2498c2ecf20Sopenharmony_ci .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions), 2508c2ecf20Sopenharmony_ci .signal = &ftm_quaddec_signals[0] 2518c2ecf20Sopenharmony_ci }, 2528c2ecf20Sopenharmony_ci { 2538c2ecf20Sopenharmony_ci .actions_list = ftm_quaddec_synapse_actions, 2548c2ecf20Sopenharmony_ci .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions), 2558c2ecf20Sopenharmony_ci .signal = &ftm_quaddec_signals[1] 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci}; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic const struct counter_count_ext ftm_quaddec_count_ext[] = { 2608c2ecf20Sopenharmony_ci COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum), 2618c2ecf20Sopenharmony_ci COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum), 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic struct counter_count ftm_quaddec_counts = { 2658c2ecf20Sopenharmony_ci .id = 0, 2668c2ecf20Sopenharmony_ci .name = "Channel 1 Count", 2678c2ecf20Sopenharmony_ci .functions_list = ftm_quaddec_count_functions, 2688c2ecf20Sopenharmony_ci .num_functions = ARRAY_SIZE(ftm_quaddec_count_functions), 2698c2ecf20Sopenharmony_ci .synapses = ftm_quaddec_count_synapses, 2708c2ecf20Sopenharmony_ci .num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses), 2718c2ecf20Sopenharmony_ci .ext = ftm_quaddec_count_ext, 2728c2ecf20Sopenharmony_ci .num_ext = ARRAY_SIZE(ftm_quaddec_count_ext) 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int ftm_quaddec_probe(struct platform_device *pdev) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct ftm_quaddec *ftm; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 2808c2ecf20Sopenharmony_ci struct resource *io; 2818c2ecf20Sopenharmony_ci int ret; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci ftm = devm_kzalloc(&pdev->dev, sizeof(*ftm), GFP_KERNEL); 2848c2ecf20Sopenharmony_ci if (!ftm) 2858c2ecf20Sopenharmony_ci return -ENOMEM; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ftm); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci io = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2908c2ecf20Sopenharmony_ci if (!io) { 2918c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get memory region\n"); 2928c2ecf20Sopenharmony_ci return -ENODEV; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ftm->pdev = pdev; 2968c2ecf20Sopenharmony_ci ftm->big_endian = of_property_read_bool(node, "big-endian"); 2978c2ecf20Sopenharmony_ci ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io)); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (!ftm->ftm_base) { 3008c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to map memory region\n"); 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci ftm->counter.name = dev_name(&pdev->dev); 3048c2ecf20Sopenharmony_ci ftm->counter.parent = &pdev->dev; 3058c2ecf20Sopenharmony_ci ftm->counter.ops = &ftm_quaddec_cnt_ops; 3068c2ecf20Sopenharmony_ci ftm->counter.counts = &ftm_quaddec_counts; 3078c2ecf20Sopenharmony_ci ftm->counter.num_counts = 1; 3088c2ecf20Sopenharmony_ci ftm->counter.signals = ftm_quaddec_signals; 3098c2ecf20Sopenharmony_ci ftm->counter.num_signals = ARRAY_SIZE(ftm_quaddec_signals); 3108c2ecf20Sopenharmony_ci ftm->counter.priv = ftm; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci mutex_init(&ftm->ftm_quaddec_mutex); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci ftm_quaddec_init(ftm); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm); 3178c2ecf20Sopenharmony_ci if (ret) 3188c2ecf20Sopenharmony_ci return ret; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ret = devm_counter_register(&pdev->dev, &ftm->counter); 3218c2ecf20Sopenharmony_ci if (ret) 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic const struct of_device_id ftm_quaddec_match[] = { 3288c2ecf20Sopenharmony_ci { .compatible = "fsl,ftm-quaddec" }, 3298c2ecf20Sopenharmony_ci {}, 3308c2ecf20Sopenharmony_ci}; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic struct platform_driver ftm_quaddec_driver = { 3338c2ecf20Sopenharmony_ci .driver = { 3348c2ecf20Sopenharmony_ci .name = "ftm-quaddec", 3358c2ecf20Sopenharmony_ci .of_match_table = ftm_quaddec_match, 3368c2ecf20Sopenharmony_ci }, 3378c2ecf20Sopenharmony_ci .probe = ftm_quaddec_probe, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cimodule_platform_driver(ftm_quaddec_driver); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>"); 3448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>"); 345