// SPDX-License-Identifier: GPL-2.0-only
/*
* A FSI master controller, using a simple GPIO bit-banging interface
*/
#include <linux/crc4.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fsi.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/irqflags.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "fsi-master.h"
#define FSI_GPIO_STD_DLY 1 /* Standard pin delay in nS */
#define LAST_ADDR_INVALID 0x1
struct fsi_master_gpio {
struct fsi_master master;
struct device *dev;
struct mutex cmd_lock; /* mutex for command ordering */
struct gpio_desc *gpio_clk;
struct gpio_desc *gpio_data;
struct gpio_desc *gpio_trans; /* Voltage translator */
struct gpio_desc *gpio_enable; /* FSI enable */
struct gpio_desc *gpio_mux; /* Mux control */
bool external_mode;
bool no_delays;
uint32_t last_addr;
uint8_t t_send_delay;
uint8_t t_echo_delay;
};
#define CREATE_TRACE_POINTS
#include <trace/events/fsi_master_gpio.h>
#define to_fsi_master_gpio(m) container_of(m, struct fsi_master_gpio, master)
struct fsi_gpio_msg {
uint64_t msg;
uint8_t bits;
};
static void clock_toggle(struct fsi_master_gpio *master, int count)
{
int i;
for (i = 0; i < count; i++) {
if (!master->no_delays)
ndelay(FSI_GPIO_STD_DLY);
gpiod_set_value(master->gpio_clk, 0);
if (!master->no_delays)
ndelay(FSI_GPIO_STD_DLY);
gpiod_set_value(master->gpio_clk, 1);
}
}
static int sda_clock_in(struct fsi_master_gpio *master)
{
int in;
if (!master->no_delays)
ndelay(FSI_GPIO_STD_DLY);
gpiod_set_value(master->gpio_clk, 0);
/* Dummy read to feed the synchronizers */
gpiod_get_value(master->gpio_data);
/* Actual data read */
in = gpiod_get_value(master->gpio_data);
if (!master->no_delays)
ndelay(FSI_GPIO_STD_DLY);
gpiod_set_value(master->gpio_clk, 1);
return in ? 1 : 0;
}
static void sda_out(struct fsi_master_gpio *master, int value)
{
gpiod_set_value(master->gpio_data, value);
}
static void set_sda_input(struct fsi_master_gpio *master)
{
gpiod_direction_input(master->gpio_data);
gpiod_set_value(master->gpio_trans, 0);
}
static void set_sda_output(struct fsi_master_gpio *master, int value)
{
gpiod_set_value(master->gpio_trans, 1);
gpiod_direction_output(master->gpio_data, value);
}
static void clock_zeros(struct fsi_master_gpio *master, int count)
{
trace_fsi_master_gpio_clock_zeros(master, count);
set_sda_output(master, 1);
clock_toggle(master, count);
}
static void echo_delay(struct fsi_master_gpio *master)
{
clock_zeros(master, master->t_echo_delay);
}
static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *msg,
uint8_t num_bits)
{
uint8_t bit, in_bit;
set_sda_input(master);
for (bit = 0; bit < num_bits; bit++) {
in_bit = sda_clock_in(master);
msg->msg <<= 1;
msg->msg |= ~in_bit & 0x1; /* Data is active low */
}
msg->bits += num_bits;
trace_fsi_master_gpio_in(master, num_bits, msg->msg);
}
static void serial_out(struct fsi_master_gpio *master,
const struct fsi_gpio_msg *cmd)
{
uint8_t bit;
uint64_t msg = ~cmd->msg; /* Data is active low */
uint64_t sda_mask = 0x1ULL << (cmd->bits - 1);
uint64_t last_bit = ~0;
int next_bit;
trace_fsi_master_gpio_out(master, cmd->bits, cmd->msg);
if (!cmd->bits) {
dev_warn(master->dev, "trying to output 0 bits\n");
return;
}
set_sda_output(master, 0);
/* Send the start bit */
sda_out(master, 0);
clock_toggle(master, 1);
/* Send the message */
for (bit = 0; bit < cmd->bits; bit++) {
next_bit = (msg & sda_mask) >> (cmd->bits - 1);
if (last_bit ^ next_bit) {
sda_out(master, next_bit);
last_bit = next_bit;
}
clock_toggle(master, 1);
msg <<= 1;
}
}
static void msg_push_bits(struct fsi_gpio_msg *msg, uint64_t data, int bits)
{
msg->msg <<= bits;
msg->msg |= data & ((1ull << bits) - 1);
msg->bits += bits