[PATCH linux v2 17/17] drivers/fsi: Add GPIO master functionality
christopher.lee.bostic at gmail.com
christopher.lee.bostic at gmail.com
Sun Oct 9 04:38:53 AEDT 2016
From: Chris Bostic <cbostic at us.ibm.com>
Add setup of the GPIO pins for FSI master function.
Set up I/O directions, define all pins needed and
set up their initial values. Define serial out
operation.
Signed-off-by: Chris Bostic <cbostic at us.ibm.com>
---
drivers/fsi/fsi-master-gpio.c | 143 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 140 insertions(+), 3 deletions(-)
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index 3b2770d..7ad1293 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -5,15 +5,29 @@
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
+#include <linux/delay.h>
#include "fsi-master.h"
#include "fsi-cfam.h"
#include "fsi-slave.h"
+#define FSI_ECHO_DELAY_CLOCKS 16 /* Number clocks for echo delay */
+#define FSI_PRE_BREAK_CLOCKS 50 /* Number clocks to prep for break */
+#define FSI_BREAK_CLOCKS 256 /* Number of clocks to issue break */
+#define FSI_POST_BREAK_CLOCKS 16000 /* Number clocks to set up cfam */
+#define FSI_INIT_CLOCKS 5000 /* Clear out any old data and states */
+
+#define FSI_GPIO_STD_DELAY 10 /* Standard GPIO delay in nS */
+ /* todo: adjust down as low as */
+ /* possible or eliminate */
+
struct fsi_master_gpio {
struct fsi_master master;
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 */
};
#define to_fsi_master_gpio(m) container_of(m, struct fsi_master_gpio, master)
@@ -23,6 +37,8 @@ struct fsi_gpio_msg {
uint8_t bits;
};
+static void serial_out(struct fsi_master_gpio *, struct fsi_gpio_msg *);
+
static int fsi_master_gpio_read(struct fsi_master *_master, int link,
uint8_t slave, uint32_t addr, void *val, size_t size)
{
@@ -42,6 +58,7 @@ static int fsi_master_gpio_write(struct fsi_master *_master, int link,
uint8_t slave, uint32_t addr, const void *val, size_t size)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ struct fsi_gpio_msg cmd;
if (link != 0)
return -ENODEV;
@@ -49,23 +66,115 @@ static int fsi_master_gpio_write(struct fsi_master *_master, int link,
/* todo: format write into a message, send, poll for response */
(void)master;
+ /* todo: fill in message */
+ cmd.msg = 0;
+ cmd.bits = 0;
+ serial_out(master, &cmd);
+
return 0;
}
+static void sda_out(struct fsi_master_gpio *master, int value)
+{
+ gpiod_set_value(master->gpio_data, value);
+
+ /* Required -do not remove */
+ ndelay(FSI_GPIO_STD_DELAY);
+}
+
+static void set_sda_output(struct fsi_master_gpio *master,
+ int value)
+{
+ gpiod_direction_output(master->gpio_data, value);
+ gpiod_direction_output(master->gpio_trans, 1);
+}
+
+static void set_clock(struct fsi_master_gpio *master)
+{
+ /* This delay is required - do not remove */
+ ndelay(FSI_GPIO_STD_DELAY);
+ gpiod_set_value(master->gpio_clk, 1);
+}
+
+static void clear_clock(struct fsi_master_gpio *master)
+{
+ /* This delay is required - do not remove */
+ ndelay(FSI_GPIO_STD_DELAY);
+ gpiod_set_value(master->gpio_clk, 0);
+}
+
+static void clock_toggle(struct fsi_master_gpio *master, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ clear_clock(master);
+ set_clock(master);
+ }
+}
+
+/*
+ * Clock out some 0's after every message to ride out line reflections
+ */
+static void echo_delay(struct fsi_master_gpio *master)
+{
+ set_sda_output(master, 0);
+ clock_toggle(master, FSI_ECHO_DELAY_CLOCKS);
+}
+
+static void serial_out(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd)
+{
+ uint16_t bit;
+ uint64_t msg = ~cmd->msg; /* Data is negative active */
+ uint64_t sda_mask = 1;
+ uint64_t last_bit = ~0;
+
+ set_sda_output(master, 0);
+
+ /* Send the start bit */
+ sda_out(master, 1);
+ clock_toggle(master, 1);
+
+ /* Send the message */
+ for (bit = 0; bit < cmd->bits; bit++) {
+ if (last_bit ^ (msg & sda_mask)) {
+ sda_out(master, msg & sda_mask);
+ last_bit = msg & sda_mask;
+ }
+ clock_toggle(master, 1);
+ msg >>= 1;
+ }
+}
+
/*
* Issue a break command on link
*/
static int fsi_master_gpio_break(struct fsi_master *_master, int link)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ uint32_t smode;
+ int rc;
if (link != 0)
return -ENODEV;
- /* todo: send the break pattern over gpio */
- (void)master;
+ set_sda_output(master, 0);
+ clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
- return 0;
+ sda_out(master, 1);
+ clock_toggle(master, FSI_BREAK_CLOCKS);
+
+ echo_delay(master);
+
+ sda_out(master, 0);
+ clock_toggle(master, FSI_POST_BREAK_CLOCKS);
+
+ udelay(200); /* wait for logic reset to take effect */
+ rc = _master->read(_master, link, 3, FSI_SLAVE_BASE + FSI_SMODE,
+ &smode, sizeof(smode));
+ dev_info(_master->dev, "smode after break:%08x rc:%d\n", smode, rc);
+
+ return rc;
}
static int fsi_master_gpio_set_smode(struct fsi_master *_master, int link,
@@ -79,6 +188,17 @@ static int fsi_master_gpio_set_smode(struct fsi_master *_master, int link,
return rc;
}
+static void fsi_master_gpio_init(struct fsi_master_gpio *master)
+{
+ gpiod_direction_output(master->gpio_mux, 1);
+ gpiod_direction_output(master->gpio_clk, 1);
+ set_sda_output(master, 1);
+ gpiod_direction_output(master->gpio_enable, 1);
+
+ /* todo: evaluate if clocks can be reduced */
+ clock_toggle(master, FSI_INIT_CLOCKS);
+}
+
static int fsi_master_gpio_probe(struct platform_device *pdev)
{
struct fsi_master_gpio *master;
@@ -98,11 +218,28 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
return PTR_ERR(gpio);
master->gpio_data = gpio;
+ gpio = devm_gpiod_get(&pdev->dev, "trans", 0);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ master->gpio_trans = gpio;
+
+ gpio = devm_gpiod_get(&pdev->dev, "enable", 0);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ master->gpio_enable = gpio;
+
+ gpio = devm_gpiod_get(&pdev->dev, "mux", 0);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ master->gpio_mux = gpio;
+
master->master.read = fsi_master_gpio_read;
master->master.write = fsi_master_gpio_write;
master->master.send_break = fsi_master_gpio_break;
master->master.set_smode = fsi_master_gpio_set_smode;
+ fsi_master_gpio_init(master);
+
return fsi_master_register(&master->master);
}
--
1.8.2.2
More information about the openbmc
mailing list