/* * Support for configuration of IO Delay module found on Texas Instruments SoCs * such as DRA7 * * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/ * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include <linux/err.h> #include <linux/init.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/regmap.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinctrl.h> #include "../core.h" #include "../devicetree.h" #define DRIVER_NAME … /** * struct ti_iodelay_reg_data - Describes the registers for the iodelay instance * @signature_mask: CONFIG_REG mask for the signature bits (see TRM) * @signature_value: CONFIG_REG signature value to be written (see TRM) * @lock_mask: CONFIG_REG mask for the lock bits (see TRM) * @lock_val: CONFIG_REG lock value for the lock bits (see TRM) * @unlock_val:CONFIG_REG unlock value for the lock bits (see TRM) * @binary_data_coarse_mask: CONFIG_REG coarse mask (see TRM) * @binary_data_fine_mask: CONFIG_REG fine mask (see TRM) * @reg_refclk_offset: Refclk register offset * @refclk_period_mask: Refclk mask * @reg_coarse_offset: Coarse register configuration offset * @coarse_delay_count_mask: Coarse delay count mask * @coarse_ref_count_mask: Coarse ref count mask * @reg_fine_offset: Fine register configuration offset * @fine_delay_count_mask: Fine delay count mask * @fine_ref_count_mask: Fine ref count mask * @reg_global_lock_offset: Global iodelay module lock register offset * @global_lock_mask: Lock mask * @global_unlock_val: Unlock value * @global_lock_val: Lock value * @reg_start_offset: Offset to iodelay registers after the CONFIG_REG_0 to 8 * @reg_nr_per_pin: Number of iodelay registers for each pin * @regmap_config: Regmap configuration for the IODelay region */ struct ti_iodelay_reg_data { … }; /** * struct ti_iodelay_reg_values - Computed io_reg configuration values (see TRM) * @coarse_ref_count: Coarse reference count * @coarse_delay_count: Coarse delay count * @fine_ref_count: Fine reference count * @fine_delay_count: Fine Delay count * @ref_clk_period: Reference Clock period * @cdpe: Coarse delay parameter * @fdpe: Fine delay parameter */ struct ti_iodelay_reg_values { … }; /** * struct ti_iodelay_cfg - Description of each configuration parameters * @offset: Configuration register offset * @a_delay: Agnostic Delay (in ps) * @g_delay: Gnostic Delay (in ps) */ struct ti_iodelay_cfg { … }; /** * struct ti_iodelay_pingroup - Structure that describes one group * @cfg: configuration array for the pin (from dt) * @ncfg: number of configuration values allocated * @config: pinconf "Config" - currently a dummy value */ struct ti_iodelay_pingroup { … }; /** * struct ti_iodelay_device - Represents information for a iodelay instance * @dev: Device pointer * @phys_base: Physical address base of the iodelay device * @reg_base: Virtual address base of the iodelay device * @regmap: Regmap for this iodelay instance * @pctl: Pinctrl device * @desc: pinctrl descriptor for pctl * @pa: pinctrl pin wise description * @reg_data: Register definition data for the IODelay instance * @reg_init_conf_values: Initial configuration values. */ struct ti_iodelay_device { … }; /** * ti_iodelay_extract() - extract bits for a field * @val: Register value * @mask: Mask * * Return: extracted value which is appropriately shifted */ static inline u32 ti_iodelay_extract(u32 val, u32 mask) { … } /** * ti_iodelay_compute_dpe() - Compute equation for delay parameter * @period: Period to use * @ref: Reference Count * @delay: Delay count * @delay_m: Delay multiplier * * Return: Computed delay parameter */ static inline u32 ti_iodelay_compute_dpe(u16 period, u16 ref, u16 delay, u16 delay_m) { … } /** * ti_iodelay_pinconf_set() - Configure the pin configuration * @iod: iodelay device * @cfg: Configuration * * Update the configuration register as per TRM and lockup once done. * *IMPORTANT NOTE* SoC TRM does recommend doing iodelay programmation only * while in Isolation. But, then, isolation also implies that every pin * on the SoC (including DDR) will be isolated out. The only benefit being * a glitchless configuration, However, the intent of this driver is purely * to support a "glitchy" configuration where applicable. * * Return: 0 in case of success, else appropriate error value */ static int ti_iodelay_pinconf_set(struct ti_iodelay_device *iod, struct ti_iodelay_cfg *cfg) { … } /** * ti_iodelay_pinconf_init_dev() - Initialize IODelay device * @iod: iodelay device * * Unlocks the iodelay region, computes the common parameters * * Return: 0 in case of success, else appropriate error value */ static int ti_iodelay_pinconf_init_dev(struct ti_iodelay_device *iod) { … } /** * ti_iodelay_pinconf_deinit_dev() - deinit the iodelay device * @iod: IODelay device * * Deinitialize the IODelay device (basically just lock the region back up. */ static void ti_iodelay_pinconf_deinit_dev(struct ti_iodelay_device *iod) { … } /** * ti_iodelay_get_pingroup() - Find the group mapped by a group selector * @iod: iodelay device * @selector: Group Selector * * Return: Corresponding group representing group selector */ static struct ti_iodelay_pingroup * ti_iodelay_get_pingroup(struct ti_iodelay_device *iod, unsigned int selector) { … } /** * ti_iodelay_offset_to_pin() - get a pin index based on the register offset * @iod: iodelay driver instance * @offset: register offset from the base */ static int ti_iodelay_offset_to_pin(struct ti_iodelay_device *iod, unsigned int offset) { … } /** * ti_iodelay_node_iterator() - Iterate iodelay node * @pctldev: Pin controller driver * @np: Device node * @pinctrl_spec: Parsed arguments from device tree * @pins: Array of pins in the pin group * @pin_index: Pin index in the pin array * @data: Pin controller driver specific data * */ static int ti_iodelay_node_iterator(struct pinctrl_dev *pctldev, struct device_node *np, const struct of_phandle_args *pinctrl_spec, int *pins, int pin_index, void *data) { … } /** * ti_iodelay_dt_node_to_map() - Map a device tree node to appropriate group * @pctldev: pinctrl device representing IODelay device * @np: Node Pointer (device tree) * @map: Pinctrl Map returned back to pinctrl framework * @num_maps: Number of maps (1) * * Maps the device tree description into a group of configuration parameters * for iodelay block entry. * * Return: 0 in case of success, else appropriate error value */ static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, unsigned int *num_maps) { … } /** * ti_iodelay_pinconf_group_get() - Get the group configuration * @pctldev: pinctrl device representing IODelay device * @selector: Group selector * @config: Configuration returned * * Return: The configuration if the group is valid, else returns -EINVAL */ static int ti_iodelay_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int selector, unsigned long *config) { … } /** * ti_iodelay_pinconf_group_set() - Configure the groups of pins * @pctldev: pinctrl device representing IODelay device * @selector: Group selector * @configs: Configurations * @num_configs: Number of configurations * * Return: 0 if all went fine, else appropriate error value. */ static int ti_iodelay_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned int selector, unsigned long *configs, unsigned int num_configs) { … } #ifdef CONFIG_DEBUG_FS /** * ti_iodelay_pin_to_offset() - get pin register offset based on the pin index * @iod: iodelay driver instance * @selector: Pin index */ static unsigned int ti_iodelay_pin_to_offset(struct ti_iodelay_device *iod, unsigned int selector) { … } static void ti_iodelay_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned int pin) { … } /** * ti_iodelay_pinconf_group_dbg_show() - show the group information * @pctldev: Show the group information * @s: Sequence file * @selector: Group selector * * Provide the configuration information of the selected group */ static void ti_iodelay_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned int selector) { … } #endif static const struct pinctrl_ops ti_iodelay_pinctrl_ops = …; static const struct pinconf_ops ti_iodelay_pinctrl_pinconf_ops = …; /** * ti_iodelay_alloc_pins() - Allocate structures needed for pins for iodelay * @dev: Device pointer * @iod: iodelay device * @base_phy: Base Physical Address * * Return: 0 if all went fine, else appropriate error value. */ static int ti_iodelay_alloc_pins(struct device *dev, struct ti_iodelay_device *iod, u32 base_phy) { … } static struct regmap_config dra7_iodelay_regmap_config = …; static struct ti_iodelay_reg_data dra7_iodelay_data = …; static const struct of_device_id ti_iodelay_of_match[] = …; MODULE_DEVICE_TABLE(of, ti_iodelay_of_match); /** * ti_iodelay_probe() - Standard probe * @pdev: platform device * * Return: 0 if all went fine, else appropriate error value. */ static int ti_iodelay_probe(struct platform_device *pdev) { … } /** * ti_iodelay_remove() - standard remove * @pdev: platform device */ static void ti_iodelay_remove(struct platform_device *pdev) { … } static struct platform_driver ti_iodelay_driver = …; module_platform_driver(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;