Commit 2d52dabb authored by Timo Schlüßler's avatar Timo Schlüßler Committed by Marc Kleine-Budde

can: mcp251x: add GPIO support

The mcp251x variants feature 3 general purpose digital inputs and 2
outputs. With this patch they are accessible through the gpio framework.
Signed-off-by: default avatarTimo Schlüßler <schluessler@krause.de>
Tested-by: default avatarTimo Schlüßler <schluessler@krause.de>
Link: https://lore.kernel.org/r/20200915223527.1417033-28-mkl@pengutronix.deSigned-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent cfc24a0a
......@@ -19,6 +19,7 @@
* Copyright 2007
*/
#include <linux/bitfield.h>
#include <linux/can/core.h>
#include <linux/can/dev.h>
#include <linux/can/led.h>
......@@ -27,6 +28,8 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/freezer.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
......@@ -52,6 +55,30 @@
#define INSTRUCTION_RTS(n) (0x80 | ((n) & 0x07))
/* MPC251x registers */
#define BFPCTRL 0x0c
# define BFPCTRL_B0BFM BIT(0)
# define BFPCTRL_B1BFM BIT(1)
# define BFPCTRL_BFM(n) (BFPCTRL_B0BFM << (n))
# define BFPCTRL_BFM_MASK GENMASK(1, 0)
# define BFPCTRL_B0BFE BIT(2)
# define BFPCTRL_B1BFE BIT(3)
# define BFPCTRL_BFE(n) (BFPCTRL_B0BFE << (n))
# define BFPCTRL_BFE_MASK GENMASK(3, 2)
# define BFPCTRL_B0BFS BIT(4)
# define BFPCTRL_B1BFS BIT(5)
# define BFPCTRL_BFS(n) (BFPCTRL_B0BFS << (n))
# define BFPCTRL_BFS_MASK GENMASK(5, 4)
#define TXRTSCTRL 0x0d
# define TXRTSCTRL_B0RTSM BIT(0)
# define TXRTSCTRL_B1RTSM BIT(1)
# define TXRTSCTRL_B2RTSM BIT(2)
# define TXRTSCTRL_RTSM(n) (TXRTSCTRL_B0RTSM << (n))
# define TXRTSCTRL_RTSM_MASK GENMASK(2, 0)
# define TXRTSCTRL_B0RTS BIT(3)
# define TXRTSCTRL_B1RTS BIT(4)
# define TXRTSCTRL_B2RTS BIT(5)
# define TXRTSCTRL_RTS(n) (TXRTSCTRL_B0RTS << (n))
# define TXRTSCTRL_RTS_MASK GENMASK(5, 3)
#define CANSTAT 0x0e
#define CANCTRL 0x0f
# define CANCTRL_REQOP_MASK 0xe0
......@@ -225,6 +252,10 @@ struct mcp251x_priv {
struct regulator *power;
struct regulator *transceiver;
struct clk *clk;
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio;
u8 reg_bfpctrl;
#endif
};
#define MCP251X_IS(_model) \
......@@ -345,6 +376,213 @@ static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
mcp251x_spi_trans(spi, 4);
}
#ifdef CONFIG_GPIOLIB
enum {
MCP251X_GPIO_TX0RTS = 0, /* inputs */
MCP251X_GPIO_TX1RTS,
MCP251X_GPIO_TX2RTS,
MCP251X_GPIO_RX0BF, /* outputs */
MCP251X_GPIO_RX1BF,
};
#define MCP251X_GPIO_INPUT_MASK \
GENMASK(MCP251X_GPIO_TX2RTS, MCP251X_GPIO_TX0RTS)
#define MCP251X_GPIO_OUTPUT_MASK \
GENMASK(MCP251X_GPIO_RX1BF, MCP251X_GPIO_RX0BF)
static const char * const mcp251x_gpio_names[] = {
[MCP251X_GPIO_TX0RTS] = "TX0RTS", /* inputs */
[MCP251X_GPIO_TX1RTS] = "TX1RTS",
[MCP251X_GPIO_TX2RTS] = "TX2RTS",
[MCP251X_GPIO_RX0BF] = "RX0BF", /* outputs */
[MCP251X_GPIO_RX1BF] = "RX1BF",
};
static inline bool mcp251x_gpio_is_input(unsigned int offset)
{
return offset <= MCP251X_GPIO_TX2RTS;
}
static int mcp251x_gpio_request(struct gpio_chip *chip,
unsigned int offset)
{
struct mcp251x_priv *priv = gpiochip_get_data(chip);
u8 val;
/* nothing to be done for inputs */
if (mcp251x_gpio_is_input(offset))
return 0;
val = BFPCTRL_BFE(offset - MCP251X_GPIO_RX0BF);
mutex_lock(&priv->mcp_lock);
mcp251x_write_bits(priv->spi, BFPCTRL, val, val);
mutex_unlock(&priv->mcp_lock);
priv->reg_bfpctrl |= val;
return 0;
}
static void mcp251x_gpio_free(struct gpio_chip *chip,
unsigned int offset)
{
struct mcp251x_priv *priv = gpiochip_get_data(chip);
u8 val;
/* nothing to be done for inputs */
if (mcp251x_gpio_is_input(offset))
return;
val = BFPCTRL_BFE(offset - MCP251X_GPIO_RX0BF);
mutex_lock(&priv->mcp_lock);
mcp251x_write_bits(priv->spi, BFPCTRL, val, 0);
mutex_unlock(&priv->mcp_lock);
priv->reg_bfpctrl &= ~val;
}
static int mcp251x_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
if (mcp251x_gpio_is_input(offset))
return GPIOF_DIR_IN;
return GPIOF_DIR_OUT;
}
static int mcp251x_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct mcp251x_priv *priv = gpiochip_get_data(chip);
u8 reg, mask, val;
if (mcp251x_gpio_is_input(offset)) {
reg = TXRTSCTRL;
mask = TXRTSCTRL_RTS(offset);
} else {
reg = BFPCTRL;
mask = BFPCTRL_BFS(offset - MCP251X_GPIO_RX0BF);
}
mutex_lock(&priv->mcp_lock);
val = mcp251x_read_reg(priv->spi, reg);
mutex_unlock(&priv->mcp_lock);
return !!(val & mask);
}
static int mcp251x_gpio_get_multiple(struct gpio_chip *chip,
unsigned long *maskp, unsigned long *bitsp)
{
struct mcp251x_priv *priv = gpiochip_get_data(chip);
unsigned long bits = 0;
u8 val;
mutex_lock(&priv->mcp_lock);
if (maskp[0] & MCP251X_GPIO_INPUT_MASK) {
val = mcp251x_read_reg(priv->spi, TXRTSCTRL);
val = FIELD_GET(TXRTSCTRL_RTS_MASK, val);
bits |= FIELD_PREP(MCP251X_GPIO_INPUT_MASK, val);
}
if (maskp[0] & MCP251X_GPIO_OUTPUT_MASK) {
val = mcp251x_read_reg(priv->spi, BFPCTRL);
val = FIELD_GET(BFPCTRL_BFS_MASK, val);
bits |= FIELD_PREP(MCP251X_GPIO_OUTPUT_MASK, val);
}
mutex_unlock(&priv->mcp_lock);
bitsp[0] = bits;
return 0;
}
static void mcp251x_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct mcp251x_priv *priv = gpiochip_get_data(chip);
u8 mask, val;
mask = BFPCTRL_BFS(offset - MCP251X_GPIO_RX0BF);
val = value ? mask : 0;
mutex_lock(&priv->mcp_lock);
mcp251x_write_bits(priv->spi, BFPCTRL, mask, val);
mutex_unlock(&priv->mcp_lock);
priv->reg_bfpctrl &= ~mask;
priv->reg_bfpctrl |= val;
}
static void
mcp251x_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *maskp, unsigned long *bitsp)
{
struct mcp251x_priv *priv = gpiochip_get_data(chip);
u8 mask, val;
mask = FIELD_GET(MCP251X_GPIO_OUTPUT_MASK, maskp[0]);
mask = FIELD_PREP(BFPCTRL_BFS_MASK, mask);
val = FIELD_GET(MCP251X_GPIO_OUTPUT_MASK, bitsp[0]);
val = FIELD_PREP(BFPCTRL_BFS_MASK, val);
if (!mask)
return;
mutex_lock(&priv->mcp_lock);
mcp251x_write_bits(priv->spi, BFPCTRL, mask, val);
mutex_unlock(&priv->mcp_lock);
priv->reg_bfpctrl &= ~mask;
priv->reg_bfpctrl |= val;
}
static void mcp251x_gpio_restore(struct spi_device *spi)
{
struct mcp251x_priv *priv = spi_get_drvdata(spi);
mcp251x_write_reg(spi, BFPCTRL, priv->reg_bfpctrl);
}
static int mcp251x_gpio_setup(struct mcp251x_priv *priv)
{
struct gpio_chip *gpio = &priv->gpio;
if (!device_property_present(&priv->spi->dev, "gpio-controller"))
return 0;
/* gpiochip handles TX[0..2]RTS and RX[0..1]BF */
gpio->label = priv->spi->modalias;
gpio->parent = &priv->spi->dev;
gpio->owner = THIS_MODULE;
gpio->request = mcp251x_gpio_request;
gpio->free = mcp251x_gpio_free;
gpio->get_direction = mcp251x_gpio_get_direction;
gpio->get = mcp251x_gpio_get;
gpio->get_multiple = mcp251x_gpio_get_multiple;
gpio->set = mcp251x_gpio_set;
gpio->set_multiple = mcp251x_gpio_set_multiple;
gpio->base = -1;
gpio->ngpio = ARRAY_SIZE(mcp251x_gpio_names);
gpio->names = mcp251x_gpio_names;
gpio->can_sleep = true;
#ifdef CONFIG_OF_GPIO
gpio->of_node = priv->spi->dev.of_node;
#endif
return devm_gpiochip_add_data(&priv->spi->dev, gpio, priv);
}
#else
static inline void mcp251x_gpio_restore(struct spi_device *spi)
{
}
static inline int mcp251x_gpio_setup(struct mcp251x_priv *priv)
{
return 0;
}
#endif
static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,
int len, int tx_buf_idx)
{
......@@ -761,6 +999,7 @@ static void mcp251x_restart_work_handler(struct work_struct *ws)
if (priv->after_suspend & AFTER_SUSPEND_POWER) {
mcp251x_hw_reset(spi);
mcp251x_setup(net, spi);
mcp251x_gpio_restore(spi);
} else {
mcp251x_hw_wake(spi);
}
......@@ -1136,6 +1375,10 @@ static int mcp251x_can_probe(struct spi_device *spi)
devm_can_led_init(net);
ret = mcp251x_gpio_setup(priv);
if (ret)
goto error_probe;
netdev_info(net, "MCP%x successfully initialized.\n", priv->model);
return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment