Christopher Bostic | 8d17c38 | 2017-03-24 16:14:04 -0500 | [diff] [blame] | 1 | From d100e52bdaef13d3f9aab162719919f660111479 Mon Sep 17 00:00:00 2001 |
| 2 | From: Christopher Bostic <cbostic@linux.vnet.ibm.com> |
| 3 | Date: Fri, 17 Mar 2017 16:00:48 -0500 |
| 4 | Subject: [PATCH linux dev-4.7 v3] drivers/fsi: Increase delay before sampling |
| 5 | input on SDA |
| 6 | To: joel@jms.id.au |
| 7 | Cc: openbmc@lists.ozlabs.org |
| 8 | |
| 9 | During high cpu loads the SDA in line can shift relative to the |
| 10 | clock signal which can corrupt the received input data. Slow |
| 11 | down the time to sample input to account for this. |
| 12 | |
| 13 | Add sysfs files to adjust the three main openfsi protocol timing |
| 14 | values: clock delay; sample delay; clock off time. |
| 15 | |
| 16 | Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com> |
| 17 | --- |
| 18 | v3 - Add sysfs files to adjust protocol timings |
| 19 | |
| 20 | v2 - Increase delay for SDA in sampling only. |
| 21 | |
| 22 | - Decrease the original delay for SDA sampling from 1us to 200ns |
| 23 | --- |
| 24 | drivers/fsi/fsi-master-gpio.c | 107 ++++++++++++++++++++++++++++++++++++++++-- |
| 25 | 1 file changed, 103 insertions(+), 4 deletions(-) |
| 26 | |
| 27 | diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c |
| 28 | index a8976d5..0907977 100644 |
| 29 | --- a/drivers/fsi/fsi-master-gpio.c |
| 30 | +++ b/drivers/fsi/fsi-master-gpio.c |
| 31 | @@ -14,6 +14,7 @@ |
| 32 | #include "fsi-master.h" |
| 33 | |
| 34 | #define FSI_GPIO_STD_DLY 1 /* Standard pin delay in nS */ |
| 35 | +#define FSI_GPIO_SDA_IN_DLY 200 /* Wait to sample SDA line, in nS */ |
| 36 | #define FSI_ECHO_DELAY_CLOCKS 16 /* Number clocks for echo delay */ |
| 37 | #define FSI_PRE_BREAK_CLOCKS 50 /* Number clocks to prep for break */ |
| 38 | #define FSI_BREAK_CLOCKS 256 /* Number of clocks to issue break */ |
| 39 | @@ -65,6 +66,10 @@ |
| 40 | |
| 41 | static DEFINE_SPINLOCK(fsi_gpio_cmd_lock); /* lock around fsi commands */ |
| 42 | |
| 43 | +static int clock_delay = FSI_GPIO_STD_DLY; |
| 44 | +static int clock_off_time = FSI_GPIO_STD_DLY; |
| 45 | +static int sample_delay = FSI_GPIO_SDA_IN_DLY; |
| 46 | + |
| 47 | struct fsi_master_gpio { |
| 48 | struct fsi_master master; |
| 49 | struct gpio_desc *gpio_clk; |
| 50 | @@ -86,9 +91,11 @@ static void clock_toggle(struct fsi_master_gpio *master, int count) |
| 51 | int i; |
| 52 | |
| 53 | for (i = 0; i < count; i++) { |
| 54 | - ndelay(FSI_GPIO_STD_DLY); |
| 55 | + udelay(clock_delay / 1000); |
| 56 | + ndelay(clock_delay % 1000); |
| 57 | gpiod_set_value(master->gpio_clk, 0); |
| 58 | - ndelay(FSI_GPIO_STD_DLY); |
| 59 | + udelay(clock_off_time / 1000); |
| 60 | + ndelay(clock_off_time % 1000); |
| 61 | gpiod_set_value(master->gpio_clk, 1); |
| 62 | } |
| 63 | } |
| 64 | @@ -97,7 +104,8 @@ static int sda_in(struct fsi_master_gpio *master) |
| 65 | { |
| 66 | int in; |
| 67 | |
| 68 | - ndelay(FSI_GPIO_STD_DLY); |
| 69 | + udelay(sample_delay / 1000); |
| 70 | + ndelay(sample_delay % 1000); |
| 71 | in = gpiod_get_value(master->gpio_data); |
| 72 | return in ? 1 : 0; |
| 73 | } |
| 74 | @@ -497,10 +505,77 @@ static ssize_t store_scan(struct device *dev, |
| 75 | |
| 76 | static DEVICE_ATTR(scan, 0200, NULL, store_scan); |
| 77 | |
| 78 | +static ssize_t clock_delay_show(struct device *dev, |
| 79 | + struct device_attribute *attr, char *buf) |
| 80 | +{ |
| 81 | + return snprintf(buf, PAGE_SIZE - 1, "%u\n", clock_delay); |
| 82 | +} |
| 83 | + |
| 84 | +static ssize_t clock_delay_store(struct device *dev, |
| 85 | + struct device_attribute *attr, |
| 86 | + const char *buf, size_t count) |
| 87 | +{ |
| 88 | + int rc; |
| 89 | + unsigned long val = 0; |
| 90 | + |
| 91 | + rc = kstrtoul(buf, 0, &val); |
| 92 | + clock_delay = val; |
| 93 | + |
| 94 | + return count; |
| 95 | +} |
| 96 | + |
| 97 | +DEVICE_ATTR(clock_delay, S_IRUGO | S_IWUSR, clock_delay_show, |
| 98 | + clock_delay_store); |
| 99 | + |
| 100 | +static ssize_t clock_off_time_show(struct device *dev, |
| 101 | + struct device_attribute *attr, char *buf) |
| 102 | +{ |
| 103 | + return snprintf(buf, PAGE_SIZE - 1, "%u\n", clock_off_time); |
| 104 | +} |
| 105 | + |
| 106 | +static ssize_t clock_off_time_store(struct device *dev, |
| 107 | + struct device_attribute *attr, |
| 108 | + const char *buf, size_t count) |
| 109 | +{ |
| 110 | + int rc; |
| 111 | + unsigned long val = 0; |
| 112 | + |
| 113 | + rc = kstrtoul(buf, 0, &val); |
| 114 | + clock_off_time = val; |
| 115 | + |
| 116 | + return count; |
| 117 | +} |
| 118 | + |
| 119 | +DEVICE_ATTR(clock_off_time, S_IRUGO | S_IWUSR, clock_off_time_show, |
| 120 | + clock_off_time_store); |
| 121 | + |
| 122 | +static ssize_t sample_delay_show(struct device *dev, |
| 123 | + struct device_attribute *attr, char *buf) |
| 124 | +{ |
| 125 | + return snprintf(buf, PAGE_SIZE - 1, "%u\n", sample_delay); |
| 126 | +} |
| 127 | + |
| 128 | +static ssize_t sample_delay_store(struct device *dev, |
| 129 | + struct device_attribute *attr, |
| 130 | + const char *buf, size_t count) |
| 131 | +{ |
| 132 | + int rc; |
| 133 | + unsigned long val = 0; |
| 134 | + |
| 135 | + rc = kstrtoul(buf, 0, &val); |
| 136 | + sample_delay = val; |
| 137 | + |
| 138 | + return count; |
| 139 | +} |
| 140 | + |
| 141 | +DEVICE_ATTR(sample_delay, S_IRUGO | S_IWUSR, sample_delay_show, |
| 142 | + sample_delay_store); |
| 143 | + |
| 144 | static int fsi_master_gpio_probe(struct platform_device *pdev) |
| 145 | { |
| 146 | struct fsi_master_gpio *master; |
| 147 | struct gpio_desc *gpio; |
| 148 | + int rc; |
| 149 | |
| 150 | master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); |
| 151 | if (!master) |
| 152 | @@ -548,8 +623,29 @@ static int fsi_master_gpio_probe(struct platform_device *pdev) |
| 153 | master->master.send_break = fsi_master_gpio_break; |
| 154 | master->master.link_enable = fsi_master_gpio_link_enable; |
| 155 | platform_set_drvdata(pdev, master); |
| 156 | + rc = device_create_file(&pdev->dev, &dev_attr_clock_off_time); |
| 157 | + if (rc) |
| 158 | + goto done; |
| 159 | + rc = device_create_file(&pdev->dev, &dev_attr_clock_delay); |
| 160 | + if (rc) |
| 161 | + goto rem_clock_off; |
| 162 | + rc = device_create_file(&pdev->dev, &dev_attr_sample_delay); |
| 163 | + if (rc) |
| 164 | + goto rem_clock_delay; |
| 165 | + rc = device_create_file(&pdev->dev, &dev_attr_scan); |
| 166 | + if (rc) |
| 167 | + goto rem_sample_delay; |
| 168 | |
| 169 | - return device_create_file(&pdev->dev, &dev_attr_scan); |
| 170 | + return rc; |
| 171 | + |
| 172 | +rem_sample_delay: |
| 173 | + device_remove_file(&pdev->dev, &dev_attr_sample_delay); |
| 174 | +rem_clock_delay: |
| 175 | + device_remove_file(&pdev->dev, &dev_attr_clock_delay); |
| 176 | +rem_clock_off: |
| 177 | + device_remove_file(&pdev->dev, &dev_attr_clock_off_time); |
| 178 | +done: |
| 179 | + return rc; |
| 180 | } |
| 181 | |
| 182 | |
| 183 | @@ -576,6 +672,9 @@ static int fsi_master_gpio_remove(struct platform_device *pdev) |
| 184 | devm_gpiod_put(&pdev->dev, master->gpio_mux); |
| 185 | } |
| 186 | fsi_master_unregister(&master->master); |
| 187 | + device_remove_file(&pdev->dev, &dev_attr_clock_off_time); |
| 188 | + device_remove_file(&pdev->dev, &dev_attr_clock_delay); |
| 189 | + device_remove_file(&pdev->dev, &dev_attr_sample_delay); |
| 190 | device_remove_file(&pdev->dev, &dev_attr_scan); |
| 191 | |
| 192 | return 0; |
| 193 | -- |
| 194 | 1.8.2.2 |
| 195 | |