diff mbox

[linux,v2,17/17] drivers/fsi: Add GPIO master functionality

Message ID 1475948333-55960-18-git-send-email-christopher.lee.bostic@gmail.com
State Changes Requested, archived
Headers show

Commit Message

christopher.lee.bostic@gmail.com Oct. 8, 2016, 5:38 p.m. UTC
From: Chris Bostic <cbostic@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@us.ibm.com>
---
 drivers/fsi/fsi-master-gpio.c | 143 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 140 insertions(+), 3 deletions(-)

Comments

Jeremy Kerr Oct. 10, 2016, 12:56 a.m. UTC | #1
Hi Chris,

> 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@us.ibm.com>

Awesome! I assume this is still in development though, right?

>  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 */
>  };

OK, you should update the device-tree binding to include these extra
GPIOs (and put their descriptions in there). Are they required for all
implementations? (is it possible that some would only have clock and
data?)

Cheers,


Jeremy
christopher.lee.bostic@gmail.com Oct. 10, 2016, 1:46 p.m. UTC | #2
On Sun, Oct 9, 2016 at 7:56 PM, Jeremy Kerr <jk@ozlabs.org> wrote:
> Hi Chris,
>
>> 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@us.ibm.com>
>
> Awesome! I assume this is still in development though, right?
>

Yes its not complete as it stands in this set.  I'll put the remainder
in version 3
of this set

>>  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 */
>>  };
>
> OK, you should update the device-tree binding to include these extra

> GPIOs (and put their descriptions in there). Are they required for all
> implementations? (is it possible that some would only have clock and
> data?)
>

OK Will update the dev-tree binding.

Good question about other implementations.  It is possible that they could have
only clock and data.  I suspect some type of enable should be used but
it wouldn't be strictly necessary.  What would you suggest given the potential
for pin differences between platforms?

Thanks,
Chris

> Cheers,
>
>
> Jeremy
Jeremy Kerr Oct. 10, 2016, 3:34 p.m. UTC | #3
Hi Chris,

> OK Will update the dev-tree binding.

OK, but we should sort out the below first:

> Good question about other implementations.  It is possible that they
> could have only clock and data.  I suspect some type of enable should
> be used but it wouldn't be strictly necessary.  What would you suggest
> given the potential for pin differences between platforms?

Make only the clk and data descriptors mandatory, and the others
optional. Enable extra functionality on the master if the other GPIOs
are present & described.

Cheers,


Jeremy
diff mbox

Patch

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);
 }