brintos

brintos / linux-shallow public Read only

0
0
Text · 8.1 KiB · 200876f Raw
330 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Flex Timer Module Quadrature decoder4 *5 * This module implements a driver for decoding the FTM quadrature6 * of ex. a LS1021A7 */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 28struct ftm_quaddec {29	struct platform_device *pdev;30	void __iomem *ftm_base;31	bool big_endian;32	struct mutex ftm_quaddec_mutex;33};34 35static 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	else40		*data = ioread32(ftm->ftm_base + offset);41}42 43static 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	else48		iowrite32(data, ftm->ftm_base + offset);49}50 51/* Hold mutex before modifying write protection state */52static 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 63static void ftm_set_write_protection(struct ftm_quaddec *ftm)64{65	FTM_FIELD_UPDATE(ftm, FTM_FMS, FTM_FMS_WPEN, 1);66}67 68static void ftm_reset_counter(struct ftm_quaddec *ftm)69{70	/* Reset hardware counter to CNTIN */71	ftm_write(ftm, FTM_CNT, 0x0);72}73 74static 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 the80	 * PWMLOAD register when FTMEN = 0.81	 * Also reset other fields to zero82	 */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 103static 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 been112	 * selected by writing to FTM_SC in init()113	 */114	ftm_set_write_protection(ftm_qua);115}116 117static int ftm_quaddec_get_prescaler(struct counter_device *counter,118				     struct counter_count *count, u32 *cnt_mode)119{120	struct ftm_quaddec *ftm = counter_priv(counter);121	uint32_t scflags;122 123	ftm_read(ftm, FTM_SC, &scflags);124 125	*cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags);126 127	return 0;128}129 130static int ftm_quaddec_set_prescaler(struct counter_device *counter,131				     struct counter_count *count, u32 cnt_mode)132{133	struct ftm_quaddec *ftm = counter_priv(counter);134 135	mutex_lock(&ftm->ftm_quaddec_mutex);136 137	ftm_clear_write_protection(ftm);138	FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode);139	ftm_set_write_protection(ftm);140 141	/* Also resets the counter as it is undefined anyway now */142	ftm_reset_counter(ftm);143 144	mutex_unlock(&ftm->ftm_quaddec_mutex);145	return 0;146}147 148static const char * const ftm_quaddec_prescaler[] = {149	"1", "2", "4", "8", "16", "32", "64", "128"150};151 152static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {153	COUNTER_SYNAPSE_ACTION_BOTH_EDGES154};155 156static const enum counter_function ftm_quaddec_count_functions[] = {157	COUNTER_FUNCTION_QUADRATURE_X4158};159 160static int ftm_quaddec_count_read(struct counter_device *counter,161				  struct counter_count *count,162				  u64 *val)163{164	struct ftm_quaddec *const ftm = counter_priv(counter);165	uint32_t cntval;166 167	ftm_read(ftm, FTM_CNT, &cntval);168 169	*val = cntval;170 171	return 0;172}173 174static int ftm_quaddec_count_write(struct counter_device *counter,175				   struct counter_count *count,176				   const u64 val)177{178	struct ftm_quaddec *const ftm = counter_priv(counter);179 180	if (val != 0) {181		dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");182		return -EINVAL;183	}184 185	ftm_reset_counter(ftm);186 187	return 0;188}189 190static int ftm_quaddec_count_function_read(struct counter_device *counter,191					   struct counter_count *count,192					   enum counter_function *function)193{194	*function = COUNTER_FUNCTION_QUADRATURE_X4;195 196	return 0;197}198 199static int ftm_quaddec_action_read(struct counter_device *counter,200				   struct counter_count *count,201				   struct counter_synapse *synapse,202				   enum counter_synapse_action *action)203{204	*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;205 206	return 0;207}208 209static const struct counter_ops ftm_quaddec_cnt_ops = {210	.count_read = ftm_quaddec_count_read,211	.count_write = ftm_quaddec_count_write,212	.function_read = ftm_quaddec_count_function_read,213	.action_read = ftm_quaddec_action_read,214};215 216static struct counter_signal ftm_quaddec_signals[] = {217	{218		.id = 0,219		.name = "Channel 1 Phase A"220	},221	{222		.id = 1,223		.name = "Channel 1 Phase B"224	}225};226 227static struct counter_synapse ftm_quaddec_count_synapses[] = {228	{229		.actions_list = ftm_quaddec_synapse_actions,230		.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),231		.signal = &ftm_quaddec_signals[0]232	},233	{234		.actions_list = ftm_quaddec_synapse_actions,235		.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),236		.signal = &ftm_quaddec_signals[1]237	}238};239 240static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler);241 242static struct counter_comp ftm_quaddec_count_ext[] = {243	COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler,244				ftm_quaddec_set_prescaler,245				ftm_quaddec_prescaler_enum),246};247 248static struct counter_count ftm_quaddec_counts = {249	.id = 0,250	.name = "Channel 1 Count",251	.functions_list = ftm_quaddec_count_functions,252	.num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),253	.synapses = ftm_quaddec_count_synapses,254	.num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),255	.ext = ftm_quaddec_count_ext,256	.num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)257};258 259static int ftm_quaddec_probe(struct platform_device *pdev)260{261	struct counter_device *counter;262	struct ftm_quaddec *ftm;263 264	struct device_node *node = pdev->dev.of_node;265	struct resource *io;266	int ret;267 268	counter = devm_counter_alloc(&pdev->dev, sizeof(*ftm));269	if (!counter)270		return -ENOMEM;271	ftm = counter_priv(counter);272 273	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);274	if (!io) {275		dev_err(&pdev->dev, "Failed to get memory region\n");276		return -ENODEV;277	}278 279	ftm->pdev = pdev;280	ftm->big_endian = of_property_read_bool(node, "big-endian");281	ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));282 283	if (!ftm->ftm_base) {284		dev_err(&pdev->dev, "Failed to map memory region\n");285		return -EINVAL;286	}287	counter->name = dev_name(&pdev->dev);288	counter->parent = &pdev->dev;289	counter->ops = &ftm_quaddec_cnt_ops;290	counter->counts = &ftm_quaddec_counts;291	counter->num_counts = 1;292	counter->signals = ftm_quaddec_signals;293	counter->num_signals = ARRAY_SIZE(ftm_quaddec_signals);294 295	mutex_init(&ftm->ftm_quaddec_mutex);296 297	ftm_quaddec_init(ftm);298 299	ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);300	if (ret)301		return ret;302 303	ret = devm_counter_add(&pdev->dev, counter);304	if (ret)305		return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");306 307	return 0;308}309 310static const struct of_device_id ftm_quaddec_match[] = {311	{ .compatible = "fsl,ftm-quaddec" },312	{},313};314 315static struct platform_driver ftm_quaddec_driver = {316	.driver = {317		.name = "ftm-quaddec",318		.of_match_table = ftm_quaddec_match,319	},320	.probe = ftm_quaddec_probe,321};322 323module_platform_driver(ftm_quaddec_driver);324 325MODULE_DESCRIPTION("Flex Timer Module Quadrature decoder");326MODULE_LICENSE("GPL");327MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");328MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");329MODULE_IMPORT_NS(COUNTER);330