/* Copyright (C) 2014-2015 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This file contains the Cygnus IOMUX driver that supports group based PINMUX
* configuration. Although PINMUX configuration is mainly group based, the
* Cygnus IOMUX controller allows certain pins to be individually muxed to GPIO
* function, and therefore be controlled by the Cygnus ASIU GPIO controller
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include "../core.h"
#include "../pinctrl-utils.h"
#define CYGNUS_NUM_IOMUX_REGS 8
#define CYGNUS_NUM_MUX_PER_REG 8
#define CYGNUS_NUM_IOMUX (CYGNUS_NUM_IOMUX_REGS * \
CYGNUS_NUM_MUX_PER_REG)
/*
* Cygnus IOMUX register description
*
* @offset: register offset for mux configuration of a group
* @shift: bit shift for mux configuration of a group
* @alt: alternate function to set to
*/
struct cygnus_mux {
unsigned int offset;
unsigned int shift;
unsigned int alt;
};
/*
* Keep track of Cygnus IOMUX configuration and prevent double configuration
*
* @cygnus_mux: Cygnus IOMUX register description
* @is_configured: flag to indicate whether a mux setting has already been
* configured
*/
struct cygnus_mux_log {
struct cygnus_mux mux;
bool is_configured;
};
/*
* Group based IOMUX configuration
*
* @name: name of the group
* @pins: array of pins used by this group
* @num_pins: total number of pins used by this group
* @mux: Cygnus group based IOMUX configuration
*/
struct cygnus_pin_group {
const char *name;
const unsigned *pins;
unsigned num_pins;
struct cygnus_mux mux;
};
/*
* Cygnus mux function and supported pin groups
*
* @name: name of the function
* @groups: array of groups that can be supported by this function
* @num_groups: total number of groups that can be supported by this function
*/
struct cygnus_pin_function {
const char *name;
const char * const *groups;
unsigned num_groups;
};
/*
* Cygnus IOMUX pinctrl core
*
* @pctl: pointer to pinctrl_dev
* @dev: pointer to device
* @base0: first I/O register base of the Cygnus IOMUX controller
* @base1: second I/O register base
* @groups: pointer to array of groups
* @num_groups: total number of groups
* @functions: pointer to array of functions
* @num_functions: total number of functions
* @mux_log: pointer to the array of mux logs
* @lock: lock to protect register access
*/
struct cygnus_pinctrl {
struct pinctrl_dev *pctl;
struct device *dev;
void __iomem *base0;
void __iomem *base1;
const struct cygnus_pin_group *groups;
unsigned num_groups;
const struct cygnus_pin_function *functions;
unsigned num_functions;
struct cygnus_mux_log *mux_log;
spinlock_t lock;
};
/*
* Certain pins can be individually muxed to GPIO function
*
* @is_supported: flag to indicate GPIO mux is supported for this pin
* @offset: register offset for GPIO mux override of a pin
* @shift: bit shift for GPIO mux override of a pin
*/
struct cygnus_gpio_mux {
int is_supported;
unsigned int offset;
unsigned int shift;
};
/*
* Description of a pin in Cygnus
*
* @pin: pin number
* @name: pin name
* @gpio_mux: GPIO override related information
*/
struct cygnus_pin {
unsigned pin;
char *name;
struct cygnus_gpio_mux gpio_mux;
};
#define CYGNUS_PIN_DESC(p, n, i, o, s) \
{ \
.pin = p, \
.name = n, \
.gpio_mux = { \
.is_supported = i, \
.offset = o, \
.shift = s, \
}, \
}
/*
* List of pins in Cygnus
*/
static struct cygnus_pin cygnus_pins[] = {
CYGNUS_PIN_DESC(0, "ext_device_reset_n", 0, 0, 0),
CYGNUS_PIN_DESC(1, "chip_mode0", 0, 0, 0),
CYGNUS_PIN_DESC(2, "chip_mode1", 0, 0, 0),
CYGNUS_PIN_DESC(3, "chip_mode2", 0, 0, 0),
CYGNUS_PIN_DESC(4, "chip_mode3", 0, 0, 0),
CYGNUS_PIN_DESC(5, "chip_mode4", 0, 0, 0),
CYGNUS_PIN_DESC(6, "bsc0_scl", 0, 0, 0),
CYGNUS_PIN_DESC(7, "bsc0_sda", 0, 0, 0),
CYGNUS_PIN_DESC(8, "bsc1_scl", 0, 0, 0),
CYGNUS_PIN_DESC(9, "bsc1_sda", 0, 0, 0),
CYGNUS_PIN_DESC(10, "d1w_dq", 1, 0x28, 0),
CYGNUS_PIN_DESC(11, "d1wowstz_l", 1, 0x4, 28),
CYGNUS_PIN_DESC(12, "gpio0", 0, 0, 0),
CYGNUS_PIN_DESC(13, "gpio1", 0, 0, 0),
CYGNUS_PIN_DESC(14, "gpio2", 0, 0, 0),
CYGNUS_PIN_DESC(15, "gpio3", 0, 0, 0),
CYGNUS_PIN_DESC(16, "gpio4", 0, 0, 0),
CYGNUS_PIN_DESC(17, "gpio5", 0, 0, 0),
CYGNUS_PIN_DESC(18, "gpio6", 0, 0, 0),
CYGNUS_PIN_DESC(19, "gpio7", 0, 0