18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Support for configuration of IO Delay module found on Texas Instruments SoCs
38c2ecf20Sopenharmony_ci * such as DRA7
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
88c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any
98c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/of_device.h>
188c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h>
198c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h>
208c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
218c2ecf20Sopenharmony_ci#include <linux/regmap.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "../core.h"
258c2ecf20Sopenharmony_ci#include "../devicetree.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define DRIVER_NAME	"ti-iodelay"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/**
308c2ecf20Sopenharmony_ci * struct ti_iodelay_reg_data - Describes the registers for the iodelay instance
318c2ecf20Sopenharmony_ci * @signature_mask: CONFIG_REG mask for the signature bits (see TRM)
328c2ecf20Sopenharmony_ci * @signature_value: CONFIG_REG signature value to be written (see TRM)
338c2ecf20Sopenharmony_ci * @lock_mask: CONFIG_REG mask for the lock bits (see TRM)
348c2ecf20Sopenharmony_ci * @lock_val: CONFIG_REG lock value for the lock bits (see TRM)
358c2ecf20Sopenharmony_ci * @unlock_val:CONFIG_REG unlock value for the lock bits (see TRM)
368c2ecf20Sopenharmony_ci * @binary_data_coarse_mask: CONFIG_REG coarse mask (see TRM)
378c2ecf20Sopenharmony_ci * @binary_data_fine_mask: CONFIG_REG fine mask (see TRM)
388c2ecf20Sopenharmony_ci * @reg_refclk_offset: Refclk register offset
398c2ecf20Sopenharmony_ci * @refclk_period_mask: Refclk mask
408c2ecf20Sopenharmony_ci * @reg_coarse_offset: Coarse register configuration offset
418c2ecf20Sopenharmony_ci * @coarse_delay_count_mask: Coarse delay count mask
428c2ecf20Sopenharmony_ci * @coarse_ref_count_mask: Coarse ref count mask
438c2ecf20Sopenharmony_ci * @reg_fine_offset: Fine register configuration offset
448c2ecf20Sopenharmony_ci * @fine_delay_count_mask: Fine delay count mask
458c2ecf20Sopenharmony_ci * @fine_ref_count_mask: Fine ref count mask
468c2ecf20Sopenharmony_ci * @reg_global_lock_offset: Global iodelay module lock register offset
478c2ecf20Sopenharmony_ci * @global_lock_mask: Lock mask
488c2ecf20Sopenharmony_ci * @global_unlock_val: Unlock value
498c2ecf20Sopenharmony_ci * @global_lock_val: Lock value
508c2ecf20Sopenharmony_ci * @reg_start_offset: Offset to iodelay registers after the CONFIG_REG_0 to 8
518c2ecf20Sopenharmony_ci * @reg_nr_per_pin: Number of iodelay registers for each pin
528c2ecf20Sopenharmony_ci * @regmap_config: Regmap configuration for the IODelay region
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_cistruct ti_iodelay_reg_data {
558c2ecf20Sopenharmony_ci	u32 signature_mask;
568c2ecf20Sopenharmony_ci	u32 signature_value;
578c2ecf20Sopenharmony_ci	u32 lock_mask;
588c2ecf20Sopenharmony_ci	u32 lock_val;
598c2ecf20Sopenharmony_ci	u32 unlock_val;
608c2ecf20Sopenharmony_ci	u32 binary_data_coarse_mask;
618c2ecf20Sopenharmony_ci	u32 binary_data_fine_mask;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	u32 reg_refclk_offset;
648c2ecf20Sopenharmony_ci	u32 refclk_period_mask;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	u32 reg_coarse_offset;
678c2ecf20Sopenharmony_ci	u32 coarse_delay_count_mask;
688c2ecf20Sopenharmony_ci	u32 coarse_ref_count_mask;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	u32 reg_fine_offset;
718c2ecf20Sopenharmony_ci	u32 fine_delay_count_mask;
728c2ecf20Sopenharmony_ci	u32 fine_ref_count_mask;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	u32 reg_global_lock_offset;
758c2ecf20Sopenharmony_ci	u32 global_lock_mask;
768c2ecf20Sopenharmony_ci	u32 global_unlock_val;
778c2ecf20Sopenharmony_ci	u32 global_lock_val;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	u32 reg_start_offset;
808c2ecf20Sopenharmony_ci	u32 reg_nr_per_pin;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	struct regmap_config *regmap_config;
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/**
868c2ecf20Sopenharmony_ci * struct ti_iodelay_reg_values - Computed io_reg configuration values (see TRM)
878c2ecf20Sopenharmony_ci * @coarse_ref_count: Coarse reference count
888c2ecf20Sopenharmony_ci * @coarse_delay_count: Coarse delay count
898c2ecf20Sopenharmony_ci * @fine_ref_count: Fine reference count
908c2ecf20Sopenharmony_ci * @fine_delay_count: Fine Delay count
918c2ecf20Sopenharmony_ci * @ref_clk_period: Reference Clock period
928c2ecf20Sopenharmony_ci * @cdpe: Coarse delay parameter
938c2ecf20Sopenharmony_ci * @fdpe: Fine delay parameter
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_cistruct ti_iodelay_reg_values {
968c2ecf20Sopenharmony_ci	u16 coarse_ref_count;
978c2ecf20Sopenharmony_ci	u16 coarse_delay_count;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	u16 fine_ref_count;
1008c2ecf20Sopenharmony_ci	u16 fine_delay_count;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	u16 ref_clk_period;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	u32 cdpe;
1058c2ecf20Sopenharmony_ci	u32 fdpe;
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/**
1098c2ecf20Sopenharmony_ci * struct ti_iodelay_cfg - Description of each configuration parameters
1108c2ecf20Sopenharmony_ci * @offset: Configuration register offset
1118c2ecf20Sopenharmony_ci * @a_delay: Agnostic Delay (in ps)
1128c2ecf20Sopenharmony_ci * @g_delay: Gnostic Delay (in ps)
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_cistruct ti_iodelay_cfg {
1158c2ecf20Sopenharmony_ci	u16 offset;
1168c2ecf20Sopenharmony_ci	u16 a_delay;
1178c2ecf20Sopenharmony_ci	u16 g_delay;
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/**
1218c2ecf20Sopenharmony_ci * struct ti_iodelay_pingroup - Structure that describes one group
1228c2ecf20Sopenharmony_ci * @cfg: configuration array for the pin (from dt)
1238c2ecf20Sopenharmony_ci * @ncfg: number of configuration values allocated
1248c2ecf20Sopenharmony_ci * @config: pinconf "Config" - currently a dummy value
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_cistruct ti_iodelay_pingroup {
1278c2ecf20Sopenharmony_ci	struct ti_iodelay_cfg *cfg;
1288c2ecf20Sopenharmony_ci	int ncfg;
1298c2ecf20Sopenharmony_ci	unsigned long config;
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/**
1338c2ecf20Sopenharmony_ci * struct ti_iodelay_device - Represents information for a iodelay instance
1348c2ecf20Sopenharmony_ci * @dev: Device pointer
1358c2ecf20Sopenharmony_ci * @phys_base: Physical address base of the iodelay device
1368c2ecf20Sopenharmony_ci * @reg_base: Virtual address base of the iodelay device
1378c2ecf20Sopenharmony_ci * @regmap: Regmap for this iodelay instance
1388c2ecf20Sopenharmony_ci * @pctl: Pinctrl device
1398c2ecf20Sopenharmony_ci * @desc: pinctrl descriptor for pctl
1408c2ecf20Sopenharmony_ci * @pa: pinctrl pin wise description
1418c2ecf20Sopenharmony_ci * @reg_data: Register definition data for the IODelay instance
1428c2ecf20Sopenharmony_ci * @reg_init_conf_values: Initial configuration values.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_cistruct ti_iodelay_device {
1458c2ecf20Sopenharmony_ci	struct device *dev;
1468c2ecf20Sopenharmony_ci	unsigned long phys_base;
1478c2ecf20Sopenharmony_ci	void __iomem *reg_base;
1488c2ecf20Sopenharmony_ci	struct regmap *regmap;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	struct pinctrl_dev *pctl;
1518c2ecf20Sopenharmony_ci	struct pinctrl_desc desc;
1528c2ecf20Sopenharmony_ci	struct pinctrl_pin_desc *pa;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	const struct ti_iodelay_reg_data *reg_data;
1558c2ecf20Sopenharmony_ci	struct ti_iodelay_reg_values reg_init_conf_values;
1568c2ecf20Sopenharmony_ci};
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/**
1598c2ecf20Sopenharmony_ci * ti_iodelay_extract() - extract bits for a field
1608c2ecf20Sopenharmony_ci * @val: Register value
1618c2ecf20Sopenharmony_ci * @mask: Mask
1628c2ecf20Sopenharmony_ci *
1638c2ecf20Sopenharmony_ci * Return: extracted value which is appropriately shifted
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic inline u32 ti_iodelay_extract(u32 val, u32 mask)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	return (val & mask) >> __ffs(mask);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/**
1718c2ecf20Sopenharmony_ci * ti_iodelay_compute_dpe() - Compute equation for delay parameter
1728c2ecf20Sopenharmony_ci * @period: Period to use
1738c2ecf20Sopenharmony_ci * @ref: Reference Count
1748c2ecf20Sopenharmony_ci * @delay: Delay count
1758c2ecf20Sopenharmony_ci * @delay_m: Delay multiplier
1768c2ecf20Sopenharmony_ci *
1778c2ecf20Sopenharmony_ci * Return: Computed delay parameter
1788c2ecf20Sopenharmony_ci */
1798c2ecf20Sopenharmony_cistatic inline u32 ti_iodelay_compute_dpe(u16 period, u16 ref, u16 delay,
1808c2ecf20Sopenharmony_ci					 u16 delay_m)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	u64 m, d;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* Handle overflow conditions */
1858c2ecf20Sopenharmony_ci	m = 10 * (u64)period * (u64)ref;
1868c2ecf20Sopenharmony_ci	d = 2 * (u64)delay * (u64)delay_m;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Truncate result back to 32 bits */
1898c2ecf20Sopenharmony_ci	return div64_u64(m, d);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/**
1938c2ecf20Sopenharmony_ci * ti_iodelay_pinconf_set() - Configure the pin configuration
1948c2ecf20Sopenharmony_ci * @iod: iodelay device
1958c2ecf20Sopenharmony_ci * @cfg: Configuration
1968c2ecf20Sopenharmony_ci *
1978c2ecf20Sopenharmony_ci * Update the configuration register as per TRM and lockup once done.
1988c2ecf20Sopenharmony_ci * *IMPORTANT NOTE* SoC TRM does recommend doing iodelay programmation only
1998c2ecf20Sopenharmony_ci * while in Isolation. But, then, isolation also implies that every pin
2008c2ecf20Sopenharmony_ci * on the SoC (including DDR) will be isolated out. The only benefit being
2018c2ecf20Sopenharmony_ci * a glitchless configuration, However, the intent of this driver is purely
2028c2ecf20Sopenharmony_ci * to support a "glitchy" configuration where applicable.
2038c2ecf20Sopenharmony_ci *
2048c2ecf20Sopenharmony_ci * Return: 0 in case of success, else appropriate error value
2058c2ecf20Sopenharmony_ci */
2068c2ecf20Sopenharmony_cistatic int ti_iodelay_pinconf_set(struct ti_iodelay_device *iod,
2078c2ecf20Sopenharmony_ci				  struct ti_iodelay_cfg *cfg)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	const struct ti_iodelay_reg_data *reg = iod->reg_data;
2108c2ecf20Sopenharmony_ci	struct ti_iodelay_reg_values *ival = &iod->reg_init_conf_values;
2118c2ecf20Sopenharmony_ci	struct device *dev = iod->dev;
2128c2ecf20Sopenharmony_ci	u32 g_delay_coarse, g_delay_fine;
2138c2ecf20Sopenharmony_ci	u32 a_delay_coarse, a_delay_fine;
2148c2ecf20Sopenharmony_ci	u32 c_elements, f_elements;
2158c2ecf20Sopenharmony_ci	u32 total_delay;
2168c2ecf20Sopenharmony_ci	u32 reg_mask, reg_val, tmp_val;
2178c2ecf20Sopenharmony_ci	int r;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* NOTE: Truncation is expected in all division below */
2208c2ecf20Sopenharmony_ci	g_delay_coarse = cfg->g_delay / 920;
2218c2ecf20Sopenharmony_ci	g_delay_fine = ((cfg->g_delay % 920) * 10) / 60;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	a_delay_coarse = cfg->a_delay / ival->cdpe;
2248c2ecf20Sopenharmony_ci	a_delay_fine = ((cfg->a_delay % ival->cdpe) * 10) / ival->fdpe;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	c_elements = g_delay_coarse + a_delay_coarse;
2278c2ecf20Sopenharmony_ci	f_elements = (g_delay_fine + a_delay_fine) / 10;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (f_elements > 22) {
2308c2ecf20Sopenharmony_ci		total_delay = c_elements * ival->cdpe + f_elements * ival->fdpe;
2318c2ecf20Sopenharmony_ci		c_elements = total_delay / ival->cdpe;
2328c2ecf20Sopenharmony_ci		f_elements = (total_delay % ival->cdpe) / ival->fdpe;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	reg_mask = reg->signature_mask;
2368c2ecf20Sopenharmony_ci	reg_val = reg->signature_value << __ffs(reg->signature_mask);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	reg_mask |= reg->binary_data_coarse_mask;
2398c2ecf20Sopenharmony_ci	tmp_val = c_elements << __ffs(reg->binary_data_coarse_mask);
2408c2ecf20Sopenharmony_ci	if (tmp_val & ~reg->binary_data_coarse_mask) {
2418c2ecf20Sopenharmony_ci		dev_err(dev, "Masking overflow of coarse elements %08x\n",
2428c2ecf20Sopenharmony_ci			tmp_val);
2438c2ecf20Sopenharmony_ci		tmp_val &= reg->binary_data_coarse_mask;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci	reg_val |= tmp_val;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	reg_mask |= reg->binary_data_fine_mask;
2488c2ecf20Sopenharmony_ci	tmp_val = f_elements << __ffs(reg->binary_data_fine_mask);
2498c2ecf20Sopenharmony_ci	if (tmp_val & ~reg->binary_data_fine_mask) {
2508c2ecf20Sopenharmony_ci		dev_err(dev, "Masking overflow of fine elements %08x\n",
2518c2ecf20Sopenharmony_ci			tmp_val);
2528c2ecf20Sopenharmony_ci		tmp_val &= reg->binary_data_fine_mask;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci	reg_val |= tmp_val;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/*
2578c2ecf20Sopenharmony_ci	 * NOTE: we leave the iodelay values unlocked - this is to work around
2588c2ecf20Sopenharmony_ci	 * situations such as those found with mmc mode change.
2598c2ecf20Sopenharmony_ci	 * However, this leaves open any unwarranted changes to padconf register
2608c2ecf20Sopenharmony_ci	 * impacting iodelay configuration. Use with care!
2618c2ecf20Sopenharmony_ci	 */
2628c2ecf20Sopenharmony_ci	reg_mask |= reg->lock_mask;
2638c2ecf20Sopenharmony_ci	reg_val |= reg->unlock_val << __ffs(reg->lock_mask);
2648c2ecf20Sopenharmony_ci	r = regmap_update_bits(iod->regmap, cfg->offset, reg_mask, reg_val);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	dev_dbg(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n",
2678c2ecf20Sopenharmony_ci		cfg->offset, cfg->a_delay, cfg->g_delay, c_elements,
2688c2ecf20Sopenharmony_ci		f_elements, reg_val);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return r;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci/**
2748c2ecf20Sopenharmony_ci * ti_iodelay_pinconf_init_dev() - Initialize IODelay device
2758c2ecf20Sopenharmony_ci * @iod: iodelay device
2768c2ecf20Sopenharmony_ci *
2778c2ecf20Sopenharmony_ci * Unlocks the iodelay region, computes the common parameters
2788c2ecf20Sopenharmony_ci *
2798c2ecf20Sopenharmony_ci * Return: 0 in case of success, else appropriate error value
2808c2ecf20Sopenharmony_ci */
2818c2ecf20Sopenharmony_cistatic int ti_iodelay_pinconf_init_dev(struct ti_iodelay_device *iod)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	const struct ti_iodelay_reg_data *reg = iod->reg_data;
2848c2ecf20Sopenharmony_ci	struct device *dev = iod->dev;
2858c2ecf20Sopenharmony_ci	struct ti_iodelay_reg_values *ival = &iod->reg_init_conf_values;
2868c2ecf20Sopenharmony_ci	u32 val;
2878c2ecf20Sopenharmony_ci	int r;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* unlock the iodelay region */
2908c2ecf20Sopenharmony_ci	r = regmap_update_bits(iod->regmap, reg->reg_global_lock_offset,
2918c2ecf20Sopenharmony_ci			       reg->global_lock_mask, reg->global_unlock_val);
2928c2ecf20Sopenharmony_ci	if (r)
2938c2ecf20Sopenharmony_ci		return r;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* Read up Recalibration sequence done by bootloader */
2968c2ecf20Sopenharmony_ci	r = regmap_read(iod->regmap, reg->reg_refclk_offset, &val);
2978c2ecf20Sopenharmony_ci	if (r)
2988c2ecf20Sopenharmony_ci		return r;
2998c2ecf20Sopenharmony_ci	ival->ref_clk_period = ti_iodelay_extract(val, reg->refclk_period_mask);
3008c2ecf20Sopenharmony_ci	dev_dbg(dev, "refclk_period=0x%04x\n", ival->ref_clk_period);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	r = regmap_read(iod->regmap, reg->reg_coarse_offset, &val);
3038c2ecf20Sopenharmony_ci	if (r)
3048c2ecf20Sopenharmony_ci		return r;
3058c2ecf20Sopenharmony_ci	ival->coarse_ref_count =
3068c2ecf20Sopenharmony_ci	    ti_iodelay_extract(val, reg->coarse_ref_count_mask);
3078c2ecf20Sopenharmony_ci	ival->coarse_delay_count =
3088c2ecf20Sopenharmony_ci	    ti_iodelay_extract(val, reg->coarse_delay_count_mask);
3098c2ecf20Sopenharmony_ci	if (!ival->coarse_delay_count) {
3108c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid Coarse delay count (0) (reg=0x%08x)\n",
3118c2ecf20Sopenharmony_ci			val);
3128c2ecf20Sopenharmony_ci		return -EINVAL;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci	ival->cdpe = ti_iodelay_compute_dpe(ival->ref_clk_period,
3158c2ecf20Sopenharmony_ci					    ival->coarse_ref_count,
3168c2ecf20Sopenharmony_ci					    ival->coarse_delay_count, 88);
3178c2ecf20Sopenharmony_ci	if (!ival->cdpe) {
3188c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid cdpe computed params = %d %d %d\n",
3198c2ecf20Sopenharmony_ci			ival->ref_clk_period, ival->coarse_ref_count,
3208c2ecf20Sopenharmony_ci			ival->coarse_delay_count);
3218c2ecf20Sopenharmony_ci		return -EINVAL;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci	dev_dbg(iod->dev, "coarse: ref=0x%04x delay=0x%04x cdpe=0x%08x\n",
3248c2ecf20Sopenharmony_ci		ival->coarse_ref_count, ival->coarse_delay_count, ival->cdpe);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	r = regmap_read(iod->regmap, reg->reg_fine_offset, &val);
3278c2ecf20Sopenharmony_ci	if (r)
3288c2ecf20Sopenharmony_ci		return r;
3298c2ecf20Sopenharmony_ci	ival->fine_ref_count =
3308c2ecf20Sopenharmony_ci	    ti_iodelay_extract(val, reg->fine_ref_count_mask);
3318c2ecf20Sopenharmony_ci	ival->fine_delay_count =
3328c2ecf20Sopenharmony_ci	    ti_iodelay_extract(val, reg->fine_delay_count_mask);
3338c2ecf20Sopenharmony_ci	if (!ival->fine_delay_count) {
3348c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid Fine delay count (0) (reg=0x%08x)\n",
3358c2ecf20Sopenharmony_ci			val);
3368c2ecf20Sopenharmony_ci		return -EINVAL;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci	ival->fdpe = ti_iodelay_compute_dpe(ival->ref_clk_period,
3398c2ecf20Sopenharmony_ci					    ival->fine_ref_count,
3408c2ecf20Sopenharmony_ci					    ival->fine_delay_count, 264);
3418c2ecf20Sopenharmony_ci	if (!ival->fdpe) {
3428c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid fdpe(0) computed params = %d %d %d\n",
3438c2ecf20Sopenharmony_ci			ival->ref_clk_period, ival->fine_ref_count,
3448c2ecf20Sopenharmony_ci			ival->fine_delay_count);
3458c2ecf20Sopenharmony_ci		return -EINVAL;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	dev_dbg(iod->dev, "fine: ref=0x%04x delay=0x%04x fdpe=0x%08x\n",
3488c2ecf20Sopenharmony_ci		ival->fine_ref_count, ival->fine_delay_count, ival->fdpe);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci/**
3548c2ecf20Sopenharmony_ci * ti_iodelay_pinconf_deinit_dev() - deinit the iodelay device
3558c2ecf20Sopenharmony_ci * @iod:	IODelay device
3568c2ecf20Sopenharmony_ci *
3578c2ecf20Sopenharmony_ci * Deinitialize the IODelay device (basically just lock the region back up.
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_cistatic void ti_iodelay_pinconf_deinit_dev(struct ti_iodelay_device *iod)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	const struct ti_iodelay_reg_data *reg = iod->reg_data;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* lock the iodelay region back again */
3648c2ecf20Sopenharmony_ci	regmap_update_bits(iod->regmap, reg->reg_global_lock_offset,
3658c2ecf20Sopenharmony_ci			   reg->global_lock_mask, reg->global_lock_val);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci/**
3698c2ecf20Sopenharmony_ci * ti_iodelay_get_pingroup() - Find the group mapped by a group selector
3708c2ecf20Sopenharmony_ci * @iod: iodelay device
3718c2ecf20Sopenharmony_ci * @selector: Group Selector
3728c2ecf20Sopenharmony_ci *
3738c2ecf20Sopenharmony_ci * Return: Corresponding group representing group selector
3748c2ecf20Sopenharmony_ci */
3758c2ecf20Sopenharmony_cistatic struct ti_iodelay_pingroup *
3768c2ecf20Sopenharmony_citi_iodelay_get_pingroup(struct ti_iodelay_device *iod, unsigned int selector)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct group_desc *g;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	g = pinctrl_generic_get_group(iod->pctl, selector);
3818c2ecf20Sopenharmony_ci	if (!g) {
3828c2ecf20Sopenharmony_ci		dev_err(iod->dev, "%s could not find pingroup %i\n", __func__,
3838c2ecf20Sopenharmony_ci			selector);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		return NULL;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return g->data;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci/**
3928c2ecf20Sopenharmony_ci * ti_iodelay_offset_to_pin() - get a pin index based on the register offset
3938c2ecf20Sopenharmony_ci * @iod: iodelay driver instance
3948c2ecf20Sopenharmony_ci * @offset: register offset from the base
3958c2ecf20Sopenharmony_ci */
3968c2ecf20Sopenharmony_cistatic int ti_iodelay_offset_to_pin(struct ti_iodelay_device *iod,
3978c2ecf20Sopenharmony_ci				    unsigned int offset)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	const struct ti_iodelay_reg_data *r = iod->reg_data;
4008c2ecf20Sopenharmony_ci	unsigned int index;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	if (offset > r->regmap_config->max_register) {
4038c2ecf20Sopenharmony_ci		dev_err(iod->dev, "mux offset out of range: 0x%x (0x%x)\n",
4048c2ecf20Sopenharmony_ci			offset, r->regmap_config->max_register);
4058c2ecf20Sopenharmony_ci		return -EINVAL;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	index = (offset - r->reg_start_offset) / r->regmap_config->reg_stride;
4098c2ecf20Sopenharmony_ci	index /= r->reg_nr_per_pin;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return index;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci/**
4158c2ecf20Sopenharmony_ci * ti_iodelay_node_iterator() - Iterate iodelay node
4168c2ecf20Sopenharmony_ci * @pctldev: Pin controller driver
4178c2ecf20Sopenharmony_ci * @np: Device node
4188c2ecf20Sopenharmony_ci * @pinctrl_spec: Parsed arguments from device tree
4198c2ecf20Sopenharmony_ci * @pins: Array of pins in the pin group
4208c2ecf20Sopenharmony_ci * @pin_index: Pin index in the pin array
4218c2ecf20Sopenharmony_ci * @data: Pin controller driver specific data
4228c2ecf20Sopenharmony_ci *
4238c2ecf20Sopenharmony_ci */
4248c2ecf20Sopenharmony_cistatic int ti_iodelay_node_iterator(struct pinctrl_dev *pctldev,
4258c2ecf20Sopenharmony_ci				    struct device_node *np,
4268c2ecf20Sopenharmony_ci				    const struct of_phandle_args *pinctrl_spec,
4278c2ecf20Sopenharmony_ci				    int *pins, int pin_index, void *data)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct ti_iodelay_device *iod;
4308c2ecf20Sopenharmony_ci	struct ti_iodelay_cfg *cfg = data;
4318c2ecf20Sopenharmony_ci	const struct ti_iodelay_reg_data *r;
4328c2ecf20Sopenharmony_ci	struct pinctrl_pin_desc *pd;
4338c2ecf20Sopenharmony_ci	int pin;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	iod = pinctrl_dev_get_drvdata(pctldev);
4368c2ecf20Sopenharmony_ci	if (!iod)
4378c2ecf20Sopenharmony_ci		return -EINVAL;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	r = iod->reg_data;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (pinctrl_spec->args_count < r->reg_nr_per_pin) {
4428c2ecf20Sopenharmony_ci		dev_err(iod->dev, "invalid args_count for spec: %i\n",
4438c2ecf20Sopenharmony_ci			pinctrl_spec->args_count);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		return -EINVAL;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* Index plus two value cells */
4498c2ecf20Sopenharmony_ci	cfg[pin_index].offset = pinctrl_spec->args[0];
4508c2ecf20Sopenharmony_ci	cfg[pin_index].a_delay = pinctrl_spec->args[1] & 0xffff;
4518c2ecf20Sopenharmony_ci	cfg[pin_index].g_delay = pinctrl_spec->args[2] & 0xffff;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	pin = ti_iodelay_offset_to_pin(iod, cfg[pin_index].offset);
4548c2ecf20Sopenharmony_ci	if (pin < 0) {
4558c2ecf20Sopenharmony_ci		dev_err(iod->dev, "could not add functions for %pOFn %ux\n",
4568c2ecf20Sopenharmony_ci			np, cfg[pin_index].offset);
4578c2ecf20Sopenharmony_ci		return -ENODEV;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci	pins[pin_index] = pin;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	pd = &iod->pa[pin];
4628c2ecf20Sopenharmony_ci	pd->drv_data = &cfg[pin_index];
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	dev_dbg(iod->dev, "%pOFn offset=%x a_delay = %d g_delay = %d\n",
4658c2ecf20Sopenharmony_ci		np, cfg[pin_index].offset, cfg[pin_index].a_delay,
4668c2ecf20Sopenharmony_ci		cfg[pin_index].g_delay);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	return 0;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci/**
4728c2ecf20Sopenharmony_ci * ti_iodelay_dt_node_to_map() - Map a device tree node to appropriate group
4738c2ecf20Sopenharmony_ci * @pctldev: pinctrl device representing IODelay device
4748c2ecf20Sopenharmony_ci * @np: Node Pointer (device tree)
4758c2ecf20Sopenharmony_ci * @map: Pinctrl Map returned back to pinctrl framework
4768c2ecf20Sopenharmony_ci * @num_maps: Number of maps (1)
4778c2ecf20Sopenharmony_ci *
4788c2ecf20Sopenharmony_ci * Maps the device tree description into a group of configuration parameters
4798c2ecf20Sopenharmony_ci * for iodelay block entry.
4808c2ecf20Sopenharmony_ci *
4818c2ecf20Sopenharmony_ci * Return: 0 in case of success, else appropriate error value
4828c2ecf20Sopenharmony_ci */
4838c2ecf20Sopenharmony_cistatic int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev,
4848c2ecf20Sopenharmony_ci				     struct device_node *np,
4858c2ecf20Sopenharmony_ci				     struct pinctrl_map **map,
4868c2ecf20Sopenharmony_ci				     unsigned int *num_maps)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	struct ti_iodelay_device *iod;
4898c2ecf20Sopenharmony_ci	struct ti_iodelay_cfg *cfg;
4908c2ecf20Sopenharmony_ci	struct ti_iodelay_pingroup *g;
4918c2ecf20Sopenharmony_ci	const char *name = "pinctrl-pin-array";
4928c2ecf20Sopenharmony_ci	int rows, *pins, error = -EINVAL, i;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	iod = pinctrl_dev_get_drvdata(pctldev);
4958c2ecf20Sopenharmony_ci	if (!iod)
4968c2ecf20Sopenharmony_ci		return -EINVAL;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	rows = pinctrl_count_index_with_args(np, name);
4998c2ecf20Sopenharmony_ci	if (rows < 0)
5008c2ecf20Sopenharmony_ci		return rows;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	*map = devm_kzalloc(iod->dev, sizeof(**map), GFP_KERNEL);
5038c2ecf20Sopenharmony_ci	if (!*map)
5048c2ecf20Sopenharmony_ci		return -ENOMEM;
5058c2ecf20Sopenharmony_ci	*num_maps = 0;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	g = devm_kzalloc(iod->dev, sizeof(*g), GFP_KERNEL);
5088c2ecf20Sopenharmony_ci	if (!g) {
5098c2ecf20Sopenharmony_ci		error = -ENOMEM;
5108c2ecf20Sopenharmony_ci		goto free_map;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	pins = devm_kcalloc(iod->dev, rows, sizeof(*pins), GFP_KERNEL);
5148c2ecf20Sopenharmony_ci	if (!pins)
5158c2ecf20Sopenharmony_ci		goto free_group;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	cfg = devm_kcalloc(iod->dev, rows, sizeof(*cfg), GFP_KERNEL);
5188c2ecf20Sopenharmony_ci	if (!cfg) {
5198c2ecf20Sopenharmony_ci		error = -ENOMEM;
5208c2ecf20Sopenharmony_ci		goto free_pins;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	for (i = 0; i < rows; i++) {
5248c2ecf20Sopenharmony_ci		struct of_phandle_args pinctrl_spec;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		error = pinctrl_parse_index_with_args(np, name, i,
5278c2ecf20Sopenharmony_ci						      &pinctrl_spec);
5288c2ecf20Sopenharmony_ci		if (error)
5298c2ecf20Sopenharmony_ci			goto free_data;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci		error = ti_iodelay_node_iterator(pctldev, np, &pinctrl_spec,
5328c2ecf20Sopenharmony_ci						 pins, i, cfg);
5338c2ecf20Sopenharmony_ci		if (error)
5348c2ecf20Sopenharmony_ci			goto free_data;
5358c2ecf20Sopenharmony_ci	}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	g->cfg = cfg;
5388c2ecf20Sopenharmony_ci	g->ncfg = i;
5398c2ecf20Sopenharmony_ci	g->config = PIN_CONFIG_END;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g);
5428c2ecf20Sopenharmony_ci	if (error < 0)
5438c2ecf20Sopenharmony_ci		goto free_data;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	(*map)->type = PIN_MAP_TYPE_CONFIGS_GROUP;
5468c2ecf20Sopenharmony_ci	(*map)->data.configs.group_or_pin = np->name;
5478c2ecf20Sopenharmony_ci	(*map)->data.configs.configs = &g->config;
5488c2ecf20Sopenharmony_ci	(*map)->data.configs.num_configs = 1;
5498c2ecf20Sopenharmony_ci	*num_maps = 1;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	return 0;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cifree_data:
5548c2ecf20Sopenharmony_ci	devm_kfree(iod->dev, cfg);
5558c2ecf20Sopenharmony_cifree_pins:
5568c2ecf20Sopenharmony_ci	devm_kfree(iod->dev, pins);
5578c2ecf20Sopenharmony_cifree_group:
5588c2ecf20Sopenharmony_ci	devm_kfree(iod->dev, g);
5598c2ecf20Sopenharmony_cifree_map:
5608c2ecf20Sopenharmony_ci	devm_kfree(iod->dev, *map);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	return error;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci/**
5668c2ecf20Sopenharmony_ci * ti_iodelay_pinconf_group_get() - Get the group configuration
5678c2ecf20Sopenharmony_ci * @pctldev: pinctrl device representing IODelay device
5688c2ecf20Sopenharmony_ci * @selector: Group selector
5698c2ecf20Sopenharmony_ci * @config: Configuration returned
5708c2ecf20Sopenharmony_ci *
5718c2ecf20Sopenharmony_ci * Return: The configuration if the group is valid, else returns -EINVAL
5728c2ecf20Sopenharmony_ci */
5738c2ecf20Sopenharmony_cistatic int ti_iodelay_pinconf_group_get(struct pinctrl_dev *pctldev,
5748c2ecf20Sopenharmony_ci					unsigned int selector,
5758c2ecf20Sopenharmony_ci					unsigned long *config)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	struct ti_iodelay_device *iod;
5788c2ecf20Sopenharmony_ci	struct ti_iodelay_pingroup *group;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	iod = pinctrl_dev_get_drvdata(pctldev);
5818c2ecf20Sopenharmony_ci	group = ti_iodelay_get_pingroup(iod, selector);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	if (!group)
5848c2ecf20Sopenharmony_ci		return -EINVAL;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	*config = group->config;
5878c2ecf20Sopenharmony_ci	return 0;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci/**
5918c2ecf20Sopenharmony_ci * ti_iodelay_pinconf_group_set() - Configure the groups of pins
5928c2ecf20Sopenharmony_ci * @pctldev: pinctrl device representing IODelay device
5938c2ecf20Sopenharmony_ci * @selector: Group selector
5948c2ecf20Sopenharmony_ci * @configs: Configurations
5958c2ecf20Sopenharmony_ci * @num_configs: Number of configurations
5968c2ecf20Sopenharmony_ci *
5978c2ecf20Sopenharmony_ci * Return: 0 if all went fine, else appropriate error value.
5988c2ecf20Sopenharmony_ci */
5998c2ecf20Sopenharmony_cistatic int ti_iodelay_pinconf_group_set(struct pinctrl_dev *pctldev,
6008c2ecf20Sopenharmony_ci					unsigned int selector,
6018c2ecf20Sopenharmony_ci					unsigned long *configs,
6028c2ecf20Sopenharmony_ci					unsigned int num_configs)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	struct ti_iodelay_device *iod;
6058c2ecf20Sopenharmony_ci	struct device *dev;
6068c2ecf20Sopenharmony_ci	struct ti_iodelay_pingroup *group;
6078c2ecf20Sopenharmony_ci	int i;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	iod = pinctrl_dev_get_drvdata(pctldev);
6108c2ecf20Sopenharmony_ci	dev = iod->dev;
6118c2ecf20Sopenharmony_ci	group = ti_iodelay_get_pingroup(iod, selector);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (num_configs != 1) {
6148c2ecf20Sopenharmony_ci		dev_err(dev, "Unsupported number of configurations %d\n",
6158c2ecf20Sopenharmony_ci			num_configs);
6168c2ecf20Sopenharmony_ci		return -EINVAL;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (*configs != PIN_CONFIG_END) {
6208c2ecf20Sopenharmony_ci		dev_err(dev, "Unsupported configuration\n");
6218c2ecf20Sopenharmony_ci		return -EINVAL;
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	for (i = 0; i < group->ncfg; i++) {
6258c2ecf20Sopenharmony_ci		if (ti_iodelay_pinconf_set(iod, &group->cfg[i]))
6268c2ecf20Sopenharmony_ci			return -ENOTSUPP;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	return 0;
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
6338c2ecf20Sopenharmony_ci/**
6348c2ecf20Sopenharmony_ci * ti_iodelay_pin_to_offset() - get pin register offset based on the pin index
6358c2ecf20Sopenharmony_ci * @iod: iodelay driver instance
6368c2ecf20Sopenharmony_ci * @selector: Pin index
6378c2ecf20Sopenharmony_ci */
6388c2ecf20Sopenharmony_cistatic unsigned int ti_iodelay_pin_to_offset(struct ti_iodelay_device *iod,
6398c2ecf20Sopenharmony_ci					     unsigned int selector)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	const struct ti_iodelay_reg_data *r = iod->reg_data;
6428c2ecf20Sopenharmony_ci	unsigned int offset;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	offset = selector * r->regmap_config->reg_stride;
6458c2ecf20Sopenharmony_ci	offset *= r->reg_nr_per_pin;
6468c2ecf20Sopenharmony_ci	offset += r->reg_start_offset;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return offset;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic void ti_iodelay_pin_dbg_show(struct pinctrl_dev *pctldev,
6528c2ecf20Sopenharmony_ci				    struct seq_file *s,
6538c2ecf20Sopenharmony_ci				    unsigned int pin)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	struct ti_iodelay_device *iod;
6568c2ecf20Sopenharmony_ci	struct pinctrl_pin_desc *pd;
6578c2ecf20Sopenharmony_ci	struct ti_iodelay_cfg *cfg;
6588c2ecf20Sopenharmony_ci	const struct ti_iodelay_reg_data *r;
6598c2ecf20Sopenharmony_ci	unsigned long offset;
6608c2ecf20Sopenharmony_ci	u32 in, oen, out;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	iod = pinctrl_dev_get_drvdata(pctldev);
6638c2ecf20Sopenharmony_ci	r = iod->reg_data;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	offset = ti_iodelay_pin_to_offset(iod, pin);
6668c2ecf20Sopenharmony_ci	pd = &iod->pa[pin];
6678c2ecf20Sopenharmony_ci	cfg = pd->drv_data;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	regmap_read(iod->regmap, offset, &in);
6708c2ecf20Sopenharmony_ci	regmap_read(iod->regmap, offset + r->regmap_config->reg_stride, &oen);
6718c2ecf20Sopenharmony_ci	regmap_read(iod->regmap, offset + r->regmap_config->reg_stride * 2,
6728c2ecf20Sopenharmony_ci		    &out);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	seq_printf(s, "%lx a: %i g: %i (%08x %08x %08x) %s ",
6758c2ecf20Sopenharmony_ci		   iod->phys_base + offset,
6768c2ecf20Sopenharmony_ci		   cfg ? cfg->a_delay : -1,
6778c2ecf20Sopenharmony_ci		   cfg ? cfg->g_delay : -1,
6788c2ecf20Sopenharmony_ci		   in, oen, out, DRIVER_NAME);
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci/**
6828c2ecf20Sopenharmony_ci * ti_iodelay_pinconf_group_dbg_show() - show the group information
6838c2ecf20Sopenharmony_ci * @pctldev: Show the group information
6848c2ecf20Sopenharmony_ci * @s: Sequence file
6858c2ecf20Sopenharmony_ci * @selector: Group selector
6868c2ecf20Sopenharmony_ci *
6878c2ecf20Sopenharmony_ci * Provide the configuration information of the selected group
6888c2ecf20Sopenharmony_ci */
6898c2ecf20Sopenharmony_cistatic void ti_iodelay_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
6908c2ecf20Sopenharmony_ci					      struct seq_file *s,
6918c2ecf20Sopenharmony_ci					      unsigned int selector)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	struct ti_iodelay_device *iod;
6948c2ecf20Sopenharmony_ci	struct ti_iodelay_pingroup *group;
6958c2ecf20Sopenharmony_ci	int i;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	iod = pinctrl_dev_get_drvdata(pctldev);
6988c2ecf20Sopenharmony_ci	group = ti_iodelay_get_pingroup(iod, selector);
6998c2ecf20Sopenharmony_ci	if (!group)
7008c2ecf20Sopenharmony_ci		return;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	for (i = 0; i < group->ncfg; i++) {
7038c2ecf20Sopenharmony_ci		struct ti_iodelay_cfg *cfg;
7048c2ecf20Sopenharmony_ci		u32 reg = 0;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci		cfg = &group->cfg[i];
7078c2ecf20Sopenharmony_ci		regmap_read(iod->regmap, cfg->offset, &reg),
7088c2ecf20Sopenharmony_ci			seq_printf(s, "\n\t0x%08x = 0x%08x (%3d, %3d)",
7098c2ecf20Sopenharmony_ci				   cfg->offset, reg, cfg->a_delay,
7108c2ecf20Sopenharmony_ci				   cfg->g_delay);
7118c2ecf20Sopenharmony_ci	}
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci#endif
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic const struct pinctrl_ops ti_iodelay_pinctrl_ops = {
7168c2ecf20Sopenharmony_ci	.get_groups_count = pinctrl_generic_get_group_count,
7178c2ecf20Sopenharmony_ci	.get_group_name = pinctrl_generic_get_group_name,
7188c2ecf20Sopenharmony_ci	.get_group_pins = pinctrl_generic_get_group_pins,
7198c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
7208c2ecf20Sopenharmony_ci	.pin_dbg_show = ti_iodelay_pin_dbg_show,
7218c2ecf20Sopenharmony_ci#endif
7228c2ecf20Sopenharmony_ci	.dt_node_to_map = ti_iodelay_dt_node_to_map,
7238c2ecf20Sopenharmony_ci};
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_cistatic const struct pinconf_ops ti_iodelay_pinctrl_pinconf_ops = {
7268c2ecf20Sopenharmony_ci	.pin_config_group_get = ti_iodelay_pinconf_group_get,
7278c2ecf20Sopenharmony_ci	.pin_config_group_set = ti_iodelay_pinconf_group_set,
7288c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
7298c2ecf20Sopenharmony_ci	.pin_config_group_dbg_show = ti_iodelay_pinconf_group_dbg_show,
7308c2ecf20Sopenharmony_ci#endif
7318c2ecf20Sopenharmony_ci};
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci/**
7348c2ecf20Sopenharmony_ci * ti_iodelay_alloc_pins() - Allocate structures needed for pins for iodelay
7358c2ecf20Sopenharmony_ci * @dev: Device pointer
7368c2ecf20Sopenharmony_ci * @iod: iodelay device
7378c2ecf20Sopenharmony_ci * @base_phy: Base Physical Address
7388c2ecf20Sopenharmony_ci *
7398c2ecf20Sopenharmony_ci * Return: 0 if all went fine, else appropriate error value.
7408c2ecf20Sopenharmony_ci */
7418c2ecf20Sopenharmony_cistatic int ti_iodelay_alloc_pins(struct device *dev,
7428c2ecf20Sopenharmony_ci				 struct ti_iodelay_device *iod, u32 base_phy)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	const struct ti_iodelay_reg_data *r = iod->reg_data;
7458c2ecf20Sopenharmony_ci	struct pinctrl_pin_desc *pin;
7468c2ecf20Sopenharmony_ci	u32 phy_reg;
7478c2ecf20Sopenharmony_ci	int nr_pins, i;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	nr_pins = ti_iodelay_offset_to_pin(iod, r->regmap_config->max_register);
7508c2ecf20Sopenharmony_ci	dev_dbg(dev, "Allocating %i pins\n", nr_pins);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	iod->pa = devm_kcalloc(dev, nr_pins, sizeof(*iod->pa), GFP_KERNEL);
7538c2ecf20Sopenharmony_ci	if (!iod->pa)
7548c2ecf20Sopenharmony_ci		return -ENOMEM;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	iod->desc.pins = iod->pa;
7578c2ecf20Sopenharmony_ci	iod->desc.npins = nr_pins;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	phy_reg = r->reg_start_offset + base_phy;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	for (i = 0; i < nr_pins; i++, phy_reg += 4) {
7628c2ecf20Sopenharmony_ci		pin = &iod->pa[i];
7638c2ecf20Sopenharmony_ci		pin->number = i;
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	return 0;
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic struct regmap_config dra7_iodelay_regmap_config = {
7708c2ecf20Sopenharmony_ci	.reg_bits = 32,
7718c2ecf20Sopenharmony_ci	.reg_stride = 4,
7728c2ecf20Sopenharmony_ci	.val_bits = 32,
7738c2ecf20Sopenharmony_ci	.max_register = 0xd1c,
7748c2ecf20Sopenharmony_ci};
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cistatic struct ti_iodelay_reg_data dra7_iodelay_data = {
7778c2ecf20Sopenharmony_ci	.signature_mask = 0x0003f000,
7788c2ecf20Sopenharmony_ci	.signature_value = 0x29,
7798c2ecf20Sopenharmony_ci	.lock_mask = 0x00000400,
7808c2ecf20Sopenharmony_ci	.lock_val = 1,
7818c2ecf20Sopenharmony_ci	.unlock_val = 0,
7828c2ecf20Sopenharmony_ci	.binary_data_coarse_mask = 0x000003e0,
7838c2ecf20Sopenharmony_ci	.binary_data_fine_mask = 0x0000001f,
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	.reg_refclk_offset = 0x14,
7868c2ecf20Sopenharmony_ci	.refclk_period_mask = 0xffff,
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	.reg_coarse_offset = 0x18,
7898c2ecf20Sopenharmony_ci	.coarse_delay_count_mask = 0xffff0000,
7908c2ecf20Sopenharmony_ci	.coarse_ref_count_mask = 0x0000ffff,
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	.reg_fine_offset = 0x1C,
7938c2ecf20Sopenharmony_ci	.fine_delay_count_mask = 0xffff0000,
7948c2ecf20Sopenharmony_ci	.fine_ref_count_mask = 0x0000ffff,
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	.reg_global_lock_offset = 0x2c,
7978c2ecf20Sopenharmony_ci	.global_lock_mask = 0x0000ffff,
7988c2ecf20Sopenharmony_ci	.global_unlock_val = 0x0000aaaa,
7998c2ecf20Sopenharmony_ci	.global_lock_val = 0x0000aaab,
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	.reg_start_offset = 0x30,
8028c2ecf20Sopenharmony_ci	.reg_nr_per_pin = 3,
8038c2ecf20Sopenharmony_ci	.regmap_config = &dra7_iodelay_regmap_config,
8048c2ecf20Sopenharmony_ci};
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_cistatic const struct of_device_id ti_iodelay_of_match[] = {
8078c2ecf20Sopenharmony_ci	{.compatible = "ti,dra7-iodelay", .data = &dra7_iodelay_data},
8088c2ecf20Sopenharmony_ci	{ /* Hopefully no more.. */ },
8098c2ecf20Sopenharmony_ci};
8108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_iodelay_of_match);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci/**
8138c2ecf20Sopenharmony_ci * ti_iodelay_probe() - Standard probe
8148c2ecf20Sopenharmony_ci * @pdev: platform device
8158c2ecf20Sopenharmony_ci *
8168c2ecf20Sopenharmony_ci * Return: 0 if all went fine, else appropriate error value.
8178c2ecf20Sopenharmony_ci */
8188c2ecf20Sopenharmony_cistatic int ti_iodelay_probe(struct platform_device *pdev)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
8218c2ecf20Sopenharmony_ci	struct device_node *np = of_node_get(dev->of_node);
8228c2ecf20Sopenharmony_ci	const struct of_device_id *match;
8238c2ecf20Sopenharmony_ci	struct resource *res;
8248c2ecf20Sopenharmony_ci	struct ti_iodelay_device *iod;
8258c2ecf20Sopenharmony_ci	int ret = 0;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	if (!np) {
8288c2ecf20Sopenharmony_ci		ret = -EINVAL;
8298c2ecf20Sopenharmony_ci		dev_err(dev, "No OF node\n");
8308c2ecf20Sopenharmony_ci		goto exit_out;
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	match = of_match_device(ti_iodelay_of_match, dev);
8348c2ecf20Sopenharmony_ci	if (!match) {
8358c2ecf20Sopenharmony_ci		ret = -EINVAL;
8368c2ecf20Sopenharmony_ci		dev_err(dev, "No DATA match\n");
8378c2ecf20Sopenharmony_ci		goto exit_out;
8388c2ecf20Sopenharmony_ci	}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	iod = devm_kzalloc(dev, sizeof(*iod), GFP_KERNEL);
8418c2ecf20Sopenharmony_ci	if (!iod) {
8428c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8438c2ecf20Sopenharmony_ci		goto exit_out;
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci	iod->dev = dev;
8468c2ecf20Sopenharmony_ci	iod->reg_data = match->data;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	/* So far We can assume there is only 1 bank of registers */
8498c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
8508c2ecf20Sopenharmony_ci	if (!res) {
8518c2ecf20Sopenharmony_ci		dev_err(dev, "Missing MEM resource\n");
8528c2ecf20Sopenharmony_ci		ret = -ENODEV;
8538c2ecf20Sopenharmony_ci		goto exit_out;
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	iod->phys_base = res->start;
8578c2ecf20Sopenharmony_ci	iod->reg_base = devm_ioremap_resource(dev, res);
8588c2ecf20Sopenharmony_ci	if (IS_ERR(iod->reg_base)) {
8598c2ecf20Sopenharmony_ci		ret = PTR_ERR(iod->reg_base);
8608c2ecf20Sopenharmony_ci		goto exit_out;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	iod->regmap = devm_regmap_init_mmio(dev, iod->reg_base,
8648c2ecf20Sopenharmony_ci					    iod->reg_data->regmap_config);
8658c2ecf20Sopenharmony_ci	if (IS_ERR(iod->regmap)) {
8668c2ecf20Sopenharmony_ci		dev_err(dev, "Regmap MMIO init failed.\n");
8678c2ecf20Sopenharmony_ci		ret = PTR_ERR(iod->regmap);
8688c2ecf20Sopenharmony_ci		goto exit_out;
8698c2ecf20Sopenharmony_ci	}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	if (ti_iodelay_pinconf_init_dev(iod))
8728c2ecf20Sopenharmony_ci		goto exit_out;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	ret = ti_iodelay_alloc_pins(dev, iod, res->start);
8758c2ecf20Sopenharmony_ci	if (ret)
8768c2ecf20Sopenharmony_ci		goto exit_out;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	iod->desc.pctlops = &ti_iodelay_pinctrl_ops;
8798c2ecf20Sopenharmony_ci	/* no pinmux ops - we are pinconf */
8808c2ecf20Sopenharmony_ci	iod->desc.confops = &ti_iodelay_pinctrl_pinconf_ops;
8818c2ecf20Sopenharmony_ci	iod->desc.name = dev_name(dev);
8828c2ecf20Sopenharmony_ci	iod->desc.owner = THIS_MODULE;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	ret = pinctrl_register_and_init(&iod->desc, dev, iod, &iod->pctl);
8858c2ecf20Sopenharmony_ci	if (ret) {
8868c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to register pinctrl\n");
8878c2ecf20Sopenharmony_ci		goto exit_out;
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, iod);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	return pinctrl_enable(iod->pctl);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ciexit_out:
8958c2ecf20Sopenharmony_ci	of_node_put(np);
8968c2ecf20Sopenharmony_ci	return ret;
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci/**
9008c2ecf20Sopenharmony_ci * ti_iodelay_remove() - standard remove
9018c2ecf20Sopenharmony_ci * @pdev: platform device
9028c2ecf20Sopenharmony_ci *
9038c2ecf20Sopenharmony_ci * Return: 0 if all went fine, else appropriate error value.
9048c2ecf20Sopenharmony_ci */
9058c2ecf20Sopenharmony_cistatic int ti_iodelay_remove(struct platform_device *pdev)
9068c2ecf20Sopenharmony_ci{
9078c2ecf20Sopenharmony_ci	struct ti_iodelay_device *iod = platform_get_drvdata(pdev);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	if (!iod)
9108c2ecf20Sopenharmony_ci		return 0;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	if (iod->pctl)
9138c2ecf20Sopenharmony_ci		pinctrl_unregister(iod->pctl);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	ti_iodelay_pinconf_deinit_dev(iod);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/* Expect other allocations to be freed by devm */
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	return 0;
9208c2ecf20Sopenharmony_ci}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_cistatic struct platform_driver ti_iodelay_driver = {
9238c2ecf20Sopenharmony_ci	.probe = ti_iodelay_probe,
9248c2ecf20Sopenharmony_ci	.remove = ti_iodelay_remove,
9258c2ecf20Sopenharmony_ci	.driver = {
9268c2ecf20Sopenharmony_ci		   .name = DRIVER_NAME,
9278c2ecf20Sopenharmony_ci		   .of_match_table = ti_iodelay_of_match,
9288c2ecf20Sopenharmony_ci	},
9298c2ecf20Sopenharmony_ci};
9308c2ecf20Sopenharmony_cimodule_platform_driver(ti_iodelay_driver);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instruments, Inc.");
9338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Pinconf driver for TI's IO Delay module");
9348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
935