2006 lines · 49.2 KB · C
RawDownload
/* * Generic device tree based pinctrl driver for one register per pin * type pinmux controllers * * Copyright (C) 2012 Texas Instruments, Inc. * * 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/init.h>#include <linux/module.h>#include <linux/io.h>#include <linux/platform_device.h>#include <linux/slab.h>#include <linux/err.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/irqchip/chained_irq.h>#include <linux/of.h>#include <linux/of_irq.h>#include <linux/seq_file.h> #include <linux/pinctrl/pinconf-generic.h>#include <linux/pinctrl/pinconf.h>#include <linux/pinctrl/pinctrl.h>#include <linux/pinctrl/pinmux.h> #include <linux/platform_data/pinctrl-single.h> #include "core.h"#include "devicetree.h"#include "pinconf.h"#include "pinmux.h" #define DRIVER_NAME			"pinctrl-single"#define PCS_OFF_DISABLED		~0U /** * struct pcs_func_vals - mux function register offset and value pair * @reg:	register virtual address * @val:	register value * @mask:	mask */struct pcs_func_vals {	void __iomem *reg;	unsigned val;	unsigned mask;}; /** * struct pcs_conf_vals - pinconf parameter, pinconf register offset * and value, enable, disable, mask * @param:	config parameter * @val:	user input bits in the pinconf register * @enable:	enable bits in the pinconf register * @disable:	disable bits in the pinconf register * @mask:	mask bits in the register value */struct pcs_conf_vals {	enum pin_config_param param;	unsigned val;	unsigned enable;	unsigned disable;	unsigned mask;}; /** * struct pcs_conf_type - pinconf property name, pinconf param pair * @name:	property name in DTS file * @param:	config parameter */struct pcs_conf_type {	const char *name;	enum pin_config_param param;}; /** * struct pcs_function - pinctrl function * @name:	pinctrl function name * @vals:	register and vals array * @nvals:	number of entries in vals array * @conf:	array of pin configurations * @nconfs:	number of pin configurations available * @node:	list node */struct pcs_function {	const char *name;	struct pcs_func_vals *vals;	unsigned nvals;	struct pcs_conf_vals *conf;	int nconfs;	struct list_head node;}; /** * struct pcs_gpiofunc_range - pin ranges with same mux value of gpio function * @offset:	offset base of pins * @npins:	number pins with the same mux value of gpio function * @gpiofunc:	mux value of gpio function * @node:	list node */struct pcs_gpiofunc_range {	unsigned offset;	unsigned npins;	unsigned gpiofunc;	struct list_head node;}; /** * struct pcs_data - wrapper for data needed by pinctrl framework * @pa:		pindesc array * @cur:	index to current element * * REVISIT: We should be able to drop this eventually by adding * support for registering pins individually in the pinctrl * framework for those drivers that don't need a static array. */struct pcs_data {	struct pinctrl_pin_desc *pa;	int cur;}; /** * struct pcs_soc_data - SoC specific settings * @flags:	initial SoC specific PCS_FEAT_xxx values * @irq:	optional interrupt for the controller * @irq_enable_mask:	optional SoC specific interrupt enable mask * @irq_status_mask:	optional SoC specific interrupt status mask * @rearm:	optional SoC specific wake-up rearm function */struct pcs_soc_data {	unsigned flags;	int irq;	unsigned irq_enable_mask;	unsigned irq_status_mask;	void (*rearm)(void);}; /** * struct pcs_device - pinctrl device instance * @res:	resources * @base:	virtual address of the controller * @saved_vals: saved values for the controller * @size:	size of the ioremapped area * @dev:	device entry * @np:		device tree node * @pctl:	pin controller device * @flags:	mask of PCS_FEAT_xxx values * @missing_nr_pinctrl_cells: for legacy binding, may go away * @socdata:	soc specific data * @lock:	spinlock for register access * @mutex:	mutex protecting the lists * @width:	bits per mux register * @fmask:	function register mask * @fshift:	function register shift * @foff:	value to turn mux off * @fmax:	max number of functions in fmask * @bits_per_mux: number of bits per mux * @bits_per_pin: number of bits per pin * @pins:	physical pins on the SoC * @gpiofuncs:	list of gpio functions * @irqs:	list of interrupt registers * @chip:	chip container for this instance * @domain:	IRQ domain for this instance * @desc:	pin controller descriptor * @read:	register read function to use * @write:	register write function to use */struct pcs_device {	struct resource *res;	void __iomem *base;	void *saved_vals;	unsigned size;	struct device *dev;	struct device_node *np;	struct pinctrl_dev *pctl;	unsigned flags;#define PCS_CONTEXT_LOSS_OFF	(1 << 3)#define PCS_QUIRK_SHARED_IRQ	(1 << 2)#define PCS_FEAT_IRQ		(1 << 1)#define PCS_FEAT_PINCONF	(1 << 0)	struct property *missing_nr_pinctrl_cells;	struct pcs_soc_data socdata;	raw_spinlock_t lock;	struct mutex mutex;	unsigned width;	unsigned fmask;	unsigned fshift;	unsigned foff;	unsigned fmax;	bool bits_per_mux;	unsigned bits_per_pin;	struct pcs_data pins;	struct list_head gpiofuncs;	struct list_head irqs;	struct irq_chip chip;	struct irq_domain *domain;	struct pinctrl_desc desc;	unsigned (*read)(void __iomem *reg);	void (*write)(unsigned val, void __iomem *reg);}; #define PCS_QUIRK_HAS_SHARED_IRQ	(pcs->flags & PCS_QUIRK_SHARED_IRQ)#define PCS_HAS_IRQ		(pcs->flags & PCS_FEAT_IRQ)#define PCS_HAS_PINCONF		(pcs->flags & PCS_FEAT_PINCONF) static int pcs_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin,			   unsigned long *config);static int pcs_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin,			   unsigned long *configs, unsigned num_configs); static enum pin_config_param pcs_bias[] = {	PIN_CONFIG_BIAS_PULL_DOWN,	PIN_CONFIG_BIAS_PULL_UP,}; /* * This lock class tells lockdep that irqchip core that this single * pinctrl can be in a different category than its parents, so it won't * report false recursion. */static struct lock_class_key pcs_lock_class; /* Class for the IRQ request mutex */static struct lock_class_key pcs_request_class; /* * REVISIT: Reads and writes could eventually use regmap or something * generic. But at least on omaps, some mux registers are performance * critical as they may need to be remuxed every time before and after * idle. Adding tests for register access width for every read and * write like regmap is doing is not desired, and caching the registers * does not help in this case. */ static unsigned int pcs_readb(void __iomem *reg){	return readb(reg);} static unsigned int pcs_readw(void __iomem *reg){	return readw(reg);} static unsigned int pcs_readl(void __iomem *reg){	return readl(reg);} static void pcs_writeb(unsigned int val, void __iomem *reg){	writeb(val, reg);} static void pcs_writew(unsigned int val, void __iomem *reg){	writew(val, reg);} static void pcs_writel(unsigned int val, void __iomem *reg){	writel(val, reg);} static unsigned int pcs_pin_reg_offset_get(struct pcs_device *pcs,					   unsigned int pin){	unsigned int mux_bytes = pcs->width / BITS_PER_BYTE; 	if (pcs->bits_per_mux) {		unsigned int pin_offset_bytes; 		pin_offset_bytes = (pcs->bits_per_pin * pin) / BITS_PER_BYTE;		return (pin_offset_bytes / mux_bytes) * mux_bytes;	} 	return pin * mux_bytes;} static unsigned int pcs_pin_shift_reg_get(struct pcs_device *pcs,					  unsigned int pin){	return (pin % (pcs->width / pcs->bits_per_pin)) * pcs->bits_per_pin;} static void pcs_pin_dbg_show(struct pinctrl_dev *pctldev,					struct seq_file *s,					unsigned pin){	struct pcs_device *pcs;	unsigned int val;	unsigned long offset;	size_t pa; 	pcs = pinctrl_dev_get_drvdata(pctldev); 	offset = pcs_pin_reg_offset_get(pcs, pin);	val = pcs->read(pcs->base + offset); 	if (pcs->bits_per_mux)		val &= pcs->fmask << pcs_pin_shift_reg_get(pcs, pin); 	pa = pcs->res->start + offset; 	seq_printf(s, "%zx %08x %s ", pa, val, DRIVER_NAME);} static void pcs_dt_free_map(struct pinctrl_dev *pctldev,				struct pinctrl_map *map, unsigned num_maps){	struct pcs_device *pcs; 	pcs = pinctrl_dev_get_drvdata(pctldev);	devm_kfree(pcs->dev, map);} static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev,				struct device_node *np_config,				struct pinctrl_map **map, unsigned *num_maps); static const struct pinctrl_ops pcs_pinctrl_ops = {	.get_groups_count = pinctrl_generic_get_group_count,	.get_group_name = pinctrl_generic_get_group_name,	.get_group_pins = pinctrl_generic_get_group_pins,	.pin_dbg_show = pcs_pin_dbg_show,	.dt_node_to_map = pcs_dt_node_to_map,	.dt_free_map = pcs_dt_free_map,}; static int pcs_get_function(struct pinctrl_dev *pctldev, unsigned pin,			    struct pcs_function **func){	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);	struct pin_desc *pdesc = pin_desc_get(pctldev, pin);	const struct pinctrl_setting_mux *setting;	const struct function_desc *function;	unsigned fselector; 	/* If pin is not described in DTS & enabled, mux_setting is NULL. */	setting = pdesc->mux_setting;	if (!setting)		return -ENOTSUPP;	fselector = setting->func;	function = pinmux_generic_get_function(pctldev, fselector);	if (!function)		return -EINVAL;	*func = function->data;	if (!(*func)) {		dev_err(pcs->dev, "%s could not find function%i\n",			__func__, fselector);		return -ENOTSUPP;	}	return 0;} static int pcs_set_mux(struct pinctrl_dev *pctldev, unsigned fselector,	unsigned group){	struct pcs_device *pcs;	const struct function_desc *function;	struct pcs_function *func;	int i; 	pcs = pinctrl_dev_get_drvdata(pctldev);	/* If function mask is null, needn't enable it. */	if (!pcs->fmask)		return 0;	function = pinmux_generic_get_function(pctldev, fselector);	if (!function)		return -EINVAL;	func = function->data;	if (!func)		return -EINVAL; 	dev_dbg(pcs->dev, "enabling %s function%i\n",		func->name, fselector); 	for (i = 0; i < func->nvals; i++) {		struct pcs_func_vals *vals;		unsigned long flags;		unsigned val, mask; 		vals = &func->vals[i];		raw_spin_lock_irqsave(&pcs->lock, flags);		val = pcs->read(vals->reg); 		if (pcs->bits_per_mux)			mask = vals->mask;		else			mask = pcs->fmask; 		val &= ~mask;		val |= (vals->val & mask);		pcs->write(val, vals->reg);		raw_spin_unlock_irqrestore(&pcs->lock, flags);	} 	return 0;} static int pcs_request_gpio(struct pinctrl_dev *pctldev,			    struct pinctrl_gpio_range *range, unsigned pin){	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);	struct pcs_gpiofunc_range *frange = NULL;	struct list_head *pos, *tmp;	unsigned data; 	/* If function mask is null, return directly. */	if (!pcs->fmask)		return -ENOTSUPP; 	list_for_each_safe(pos, tmp, &pcs->gpiofuncs) {		u32 offset; 		frange = list_entry(pos, struct pcs_gpiofunc_range, node);		if (pin >= frange->offset + frange->npins			|| pin < frange->offset)			continue; 		offset = pcs_pin_reg_offset_get(pcs, pin); 		if (pcs->bits_per_mux) {			int pin_shift = pcs_pin_shift_reg_get(pcs, pin); 			data = pcs->read(pcs->base + offset);			data &= ~(pcs->fmask << pin_shift);			data |= frange->gpiofunc << pin_shift;			pcs->write(data, pcs->base + offset);		} else {			data = pcs->read(pcs->base + offset);			data &= ~pcs->fmask;			data |= frange->gpiofunc;			pcs->write(data, pcs->base + offset);		}		break;	}	return 0;} static const struct pinmux_ops pcs_pinmux_ops = {	.get_functions_count = pinmux_generic_get_function_count,	.get_function_name = pinmux_generic_get_function_name,	.get_function_groups = pinmux_generic_get_function_groups,	.set_mux = pcs_set_mux,	.gpio_request_enable = pcs_request_gpio,}; /* Clear BIAS value */static void pcs_pinconf_clear_bias(struct pinctrl_dev *pctldev, unsigned pin){	unsigned long config;	int i;	for (i = 0; i < ARRAY_SIZE(pcs_bias); i++) {		config = pinconf_to_config_packed(pcs_bias[i], 0);		pcs_pinconf_set(pctldev, pin, &config, 1);	}} /* * Check whether PIN_CONFIG_BIAS_DISABLE is valid. * It's depend on that PULL_DOWN & PULL_UP configs are all invalid. */static bool pcs_pinconf_bias_disable(struct pinctrl_dev *pctldev, unsigned pin){	unsigned long config;	int i; 	for (i = 0; i < ARRAY_SIZE(pcs_bias); i++) {		config = pinconf_to_config_packed(pcs_bias[i], 0);		if (!pcs_pinconf_get(pctldev, pin, &config))			goto out;	}	return true;out:	return false;} static int pcs_pinconf_get(struct pinctrl_dev *pctldev,				unsigned pin, unsigned long *config){	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);	struct pcs_function *func;	enum pin_config_param param;	unsigned offset = 0, data = 0, i, j;	int ret; 	ret = pcs_get_function(pctldev, pin, &func);	if (ret)		return ret; 	for (i = 0; i < func->nconfs; i++) {		param = pinconf_to_config_param(*config);		if (param == PIN_CONFIG_BIAS_DISABLE) {			if (pcs_pinconf_bias_disable(pctldev, pin)) {				*config = 0;				return 0;

Lines 1–500 of 2006.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t