[PATCH] drivers: misc: Support for POST card display

Jaghathiswari Rankappagounder Natarajan jaghu at google.com
Wed Oct 12 15:31:52 AEDT 2016


Added a character driver and a platform driver to display Port80 codes
(written by the host) and additional codes (generated by the BMC)
on a POST card (two seven segment LEDs) ;
Tested that the post codes are displayed properly
on the POST card by writing to the character device.

Signed-off-by: Jaghathiswari Rankappagounder Natarajan <jaghu at google.com>
---
 .../devicetree/bindings/misc/postcard-disp.txt     |  18 ++
 arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts         |   8 +
 drivers/misc/Makefile                              |   2 +
 drivers/misc/update-bmc-postcode.c                 | 117 +++++++++++
 drivers/misc/update-postcard.c                     | 219 +++++++++++++++++++++
 drivers/misc/update-postcard.h                     |   1 +
 6 files changed, 365 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/postcard-disp.txt
 create mode 100644 drivers/misc/update-bmc-postcode.c
 create mode 100644 drivers/misc/update-postcard.c
 create mode 100644 drivers/misc/update-postcard.h

diff --git a/Documentation/devicetree/bindings/misc/postcard-disp.txt b/Documentation/devicetree/bindings/misc/postcard-disp.txt
new file mode 100644
index 0000000..164449e
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/postcard-disp.txt
@@ -0,0 +1,18 @@
+POST card display (two seven segment LEDs) connected to GPIO lines
+
+Required properties:
+- compatible : should be "postcard-display".
+- clk-gpios :  Should specify the GPIO pin connected to the Clock line on the POST card.
+- data-gpios : Should specify the GPIO pin connected to Data line on the POST card.
+- clear-gpios : Should specify the GPIO pin connected to Clear line on the POST card.
+
+Examples:
+
+#include <dt-bindings/gpio/gpio.h>
+
+postcard {
+	compatible = "postcard-display";
+	clk-gpios = <&gpio 0 GPIO_ACTIVE_LOW>;
+	data-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+	clear-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+};
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts b/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
index 4c4754b..d555ff1 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
@@ -1,6 +1,7 @@
 /dts-v1/;

 #include "aspeed-g5.dtsi"
+#include <dt-bindings/gpio/aspeed-gpio.h>

 / {
 	model = "Zaius BMC";
@@ -58,6 +59,13 @@
 			};
 		};
 	};
+
+	postcard {
+		compatible = "postcard-display";
+		clk-gpios = <&gpio ASPEED_GPIO(J, 0) GPIO_ACTIVE_HIGH>;
+		data-gpios = <&gpio ASPEED_GPIO(J, 2) GPIO_ACTIVE_HIGH>;
+		clr-gpios = <&gpio ASPEED_GPIO(J, 1) GPIO_ACTIVE_HIGH>;
+	};
 };

 &uart5 {
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 724861b..04184b9 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -58,3 +58,5 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_PANEL)             += panel.o
 obj-$(CONFIG_ASPEED_BT_IPMI_HOST)	+= bt-host.o
+obj-y				+= update-postcard.o
+obj-y				+= update-bmc-postcode.o
diff --git a/drivers/misc/update-bmc-postcode.c b/drivers/misc/update-bmc-postcode.c
new file mode 100644
index 0000000..0b5ded5
--- /dev/null
+++ b/drivers/misc/update-bmc-postcode.c
@@ -0,0 +1,117 @@
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+
+#include "update-postcard.h"
+
+#define MAX_POSTCODE_SIZE 3
+
+static dev_t bmc_state_dev;
+static struct cdev c_dev;
+static struct class *bmc_state_class;
+
+static int bmc_state_open(struct inode *i, struct file *f)
+{
+	return 0;
+}
+
+static int bmc_state_close(struct inode *i, struct file *f)
+{
+	return 0;
+}
+
+static ssize_t bmc_state_read(struct file *f, char __user *buf, size_t
+				len, loff_t *off)
+{
+	return 0;
+}
+
+/* Write post code from bmc to the correct variable */
+static ssize_t bmc_state_write(struct file *f, const char __user *buf,
+				size_t len, loff_t *off)
+{
+	char tmp[MAX_POSTCODE_SIZE];
+	int length = len - 1;
+	int i;
+
+	if (length != MAX_POSTCODE_SIZE) {
+		return -EINVAL;
+	}
+
+	if (copy_from_user(tmp, buf, length) != 0) {
+		return -EFAULT;
+	}
+
+	for (i = 0; i < MAX_POSTCODE_SIZE; i++) {
+		if (!isxdigit(tmp[i]))
+			return -EINVAL;
+	}
+
+	update_postcode(tmp);
+	return len;
+}
+
+static const struct file_operations bmc_state_fops = {
+
+	.owner = THIS_MODULE,
+	.open = bmc_state_open,
+	.release = bmc_state_close,
+	.read = bmc_state_read,
+	.write = bmc_state_write
+};
+
+static int __init update_bmc_pc_init(void) /* Constructor */
+{
+	if (alloc_chrdev_region(&bmc_state_dev, 0, 1, "bmc_state") < 0) {
+		return -1;
+	}
+
+	bmc_state_class = class_create(THIS_MODULE, "bmc_state");
+	if (bmc_state_class == NULL) {
+		goto unregister;
+		return -1;
+	}
+
+	if (device_create(bmc_state_class, NULL, bmc_state_dev,
+		NULL, "current_state") == NULL) {
+		goto class_destroy;
+		return -1;
+	}
+
+	cdev_init(&c_dev, &bmc_state_fops);
+	if (cdev_add(&c_dev, bmc_state_dev, 1) == -1) {
+		goto device_destroy;
+		return -1;
+	}
+
+	return 0;
+
+device_destroy:
+		device_destroy(bmc_state_class, bmc_state_dev);
+class_destroy:
+		class_destroy(bmc_state_class);
+unregister:
+		unregister_chrdev_region(bmc_state_dev, 1);
+	return -1;
+}
+
+static void __exit update_bmc_pc_exit(void)
+{
+	cdev_del(&c_dev);
+	device_destroy(bmc_state_class, bmc_state_dev);
+	class_destroy(bmc_state_class);
+	unregister_chrdev_region(bmc_state_dev, 1);
+}
+
+module_init(update_bmc_pc_init);
+module_exit(update_bmc_pc_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jaghathiswari Rankappagounder Natarajan <jaghu at google.com>");
+MODULE_DESCRIPTION("Character driver - update bmc postcode");
diff --git a/drivers/misc/update-postcard.c b/drivers/misc/update-postcard.c
new file mode 100644
index 0000000..4b51d70
--- /dev/null
+++ b/drivers/misc/update-postcard.c
@@ -0,0 +1,219 @@
+/* Display Port80 codes (written by the host) and
+ * additional codes (generated by the EC) to two seven segment LEDs
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/sizes.h>
+#include <linux/map_to_7segment.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+#include <linux/gpio/consumer.h>
+
+#define POST_CODE_UPDATE_INTVL 1000
+
+#define MAX_PC_SIZE 3
+
+#define LED_DOT 0x01
+
+#define CLOCK_GPIO_NAME "clk"
+#define DATA_GPIO_NAME "data"
+#define CLEAR_GPIO_NAME "clr"
+
+static struct mutex mutex;
+
+static struct timer_list update_timer;
+
+static char curr_postcode[3];
+static u8 pc_valid;
+
+static struct gpio_desc *clk_gpio;
+static struct gpio_desc *data_gpio;
+static struct gpio_desc *clr_gpio;
+
+/*
+ * 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
+ *  _       _   _       _   _   _   _   _   _       _       _   _
+ * | |   |  _|  _| |_| |_  |_    | |_| |_| |_| |_  |    _| |_  |_
+ * |_|   | |_   _|   |  _| |_|   | |_|   | | | |_| |_  |_| |_  |
+ *
+ * data[7:1] = led[a:g]
+ * lookup table used for both bytes of lpc post code and lower byte of
+ * bmc post code
+ */
+const u8 seven_seg_bits[] = {
+	0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0,
+	0xFE, 0xF6, 0xEE, 0x3E, 0x9C, 0x7A, 0x9E, 0x8E
+	};
+
+/*
+ * 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
+ *      _       _   _                              _            _
+ *     |   |_  |_| |_  _   _   _   _   _   _   _  |_    _|  _| | |
+ *     |_  |_  |   |                               _|  |_| |_| | |
+ *
+ * data[7:1] = led[a:g]
+ * lookup table used for higher byte of bmc post code
+ */
+const u8 special_seven_seg_bits[] = {
+	0x00, 0x9C, 0x1E, 0xCE, 0x8E, 0x02, 0x02, 0x02,
+	0x02, 0x02, 0x02, 0x02, 0xB6, 0x7A, 0x7A, 0xEC
+	};
+
+void update_postcode(char *buf)
+{
+	mutex_lock(&mutex);
+	strncpy(curr_postcode, buf, sizeof(curr_postcode));
+	mutex_unlock(&mutex);
+	pc_valid = 1;
+}
+EXPORT_SYMBOL(update_postcode);
+
+/* convert postcode to led pattern
+ * 7-bits used for each 7-seg display and
+ * 1 bit used for the 'dot' on both digits.
+ *
+ *  @param N/A
+ *  @return N/A
+ */
+static u16 postcode_to_led_signal(void)
+{
+	u8 low_display;
+	u8 high_display;
+	u16 led_value;
+	char buf[3];
+
+	mutex_lock(&mutex);
+	strncpy(buf, curr_postcode, sizeof(buf));
+	mutex_unlock(&mutex);
+
+	low_display = seven_seg_bits[hex_to_bin(buf[2])];
+
+	high_display = (buf[0] == '1') ?
+		special_seven_seg_bits[hex_to_bin(buf[1])] :
+		seven_seg_bits[hex_to_bin(buf[1])];
+
+	led_value = low_display | (high_display << 8);
+	if (buf[0] == '1') {
+		led_value |= LED_DOT | (LED_DOT << 8);
+	}
+
+	return led_value;
+}
+
+static void update_sgpio(void)
+{
+	u16 led_signal;
+	int i;
+
+	/* Convert two bytes of post code to 16 bit led pattern */
+	led_signal = postcode_to_led_signal();
+
+	/* Set the clear signal to low */
+	gpiod_set_value(clr_gpio, 0);
+	udelay(1);
+	/* Set the clear clear to high */
+	gpiod_set_value(clr_gpio, 1);
+	udelay(1);
+
+	/* Bitbang the 16 bit led pattern
+	 * 7-bits used for each 7-seg display
+	 * 1 bit used for the 'dot' on both digits
+	 */
+	for (i = 0; i < 16; i++) {
+		if (led_signal & 0x01) {
+			/* Set the data signal to high */
+			gpiod_set_value(data_gpio, 1);
+		} else {
+			/* Set the data signal to low */
+			gpiod_set_value(data_gpio, 0);
+		}
+		udelay(1);
+
+		/* Set the clock signal to low */
+		gpiod_set_value(clk_gpio, 0);
+		udelay(1);
+		/* Set the clock signal to high */
+		gpiod_set_value(clk_gpio, 1);
+		udelay(1);
+
+		led_signal >>= 1;
+	}
+}
+
+static void update_timer_handler(unsigned long data)
+{
+	if (pc_valid == 1) {
+		update_sgpio();
+	}
+	mod_timer(&update_timer,
+		jiffies + msecs_to_jiffies(POST_CODE_UPDATE_INTVL));
+}
+
+static const struct of_device_id of_postcard_match[] = {
+		{ .compatible = "postcard-display" },
+			{},
+};
+
+MODULE_DEVICE_TABLE(of, of_postcard_match);
+
+static int postcard_probe(struct platform_device *pdev)
+{
+	int result;
+	struct device *dev = &pdev->dev;
+
+	/* Requesting the clock gpio */
+	clk_gpio = devm_gpiod_get(dev, CLOCK_GPIO_NAME,
+		GPIOD_OUT_HIGH);
+	if (IS_ERR(clk_gpio))
+		return PTR_ERR(data_gpio);
+
+	/* Requesting the data gpio */
+	data_gpio = devm_gpiod_get(dev, DATA_GPIO_NAME,
+		GPIOD_OUT_HIGH);
+	if (IS_ERR(data_gpio))
+		return PTR_ERR(data_gpio);
+
+	/* Requesting the clear gpio */
+	clr_gpio = devm_gpiod_get(dev, CLEAR_GPIO_NAME,
+		GPIOD_OUT_HIGH);
+	if (IS_ERR(clr_gpio))
+		return PTR_ERR(clr_gpio);
+
+	/* Start timer to update post code every second */
+	setup_timer(&update_timer, update_timer_handler, 0);
+	result = mod_timer(&update_timer,
+		jiffies + msecs_to_jiffies(POST_CODE_UPDATE_INTVL));
+
+	if (result)
+		return result;
+
+	mutex_init(&mutex);
+
+	return 0;
+}
+
+static void postcard_remove(struct platform_device *pdev)
+{
+	del_timer(&update_timer);
+}
+
+static struct platform_driver postcard_display_driver = {
+	.probe		= postcard_probe,
+	.shutdown	= postcard_remove,
+	.driver		= {
+		.name	= "postcard-display",
+		.of_match_table = of_postcard_match,
+	},
+};
+
+module_platform_driver(postcard_display_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jaghathiswari Rankappagounder Natarajan <jaghu at google.com>");
+MODULE_DESCRIPTION("Post card display update driver");
diff --git a/drivers/misc/update-postcard.h b/drivers/misc/update-postcard.h
new file mode 100644
index 0000000..ef82e9091d
--- /dev/null
+++ b/drivers/misc/update-postcard.h
@@ -0,0 +1 @@
+void update_postcode(char *buf);
--
2.8.0.rc3.226.g39d4020



More information about the openbmc mailing list