blob: b166c78b81911f4557a533d906f845c028e02896 [file] [log] [blame]
George Hungd9f9d7e2020-07-16 22:03:39 +08001From ea3e732d2c4a609e621346a96d37713820640196 Mon Sep 17 00:00:00 2001
2From: Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>
3Date: Fri, 27 Jul 2018 12:36:24 -0700
4Subject: [PATCH 3/3] drivers: misc: Platform driver for seven segment display
5 support
6
7Platform device driver which provides an API for displaying on two
87-segment displays, and implements the required bit-banging.
9The hardware assumed is 74HC164 wired to two 7-segment displays.
10
11Signed-off-by: Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>
12Signed-off-by: Kun Yi <kunyi@google.com>
13Signed-off-by: Benjamin Fair <benjaminfair@google.com>
14---
15 drivers/misc/Kconfig | 8 ++
16# drivers/misc/Makefile | 2 ++
17 drivers/misc/seven_seg_gpio.c | 205 ++++++++++++++++++++++++++++++++++
18 3 files changed, 215 insertions(+)
19 create mode 100644 drivers/misc/seven_seg_gpio.c
20
21diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
22index d443de886346..e10984e3288f 100644
23--- a/drivers/misc/Kconfig
24+++ b/drivers/misc/Kconfig
25@@ -481,6 +481,14 @@ config SEVEN_SEGMENT_DISPLAY
26 any conversion methods necessary to map the user input
27 to two 7-segment displays.
28
29+config SEVEN_SEGMENT_GPIO
30+ tristate "Platform driver to update seven segment display"
31+ depends on SEVEN_SEGMENT_DISPLAY
32+ help
33+ Platform device driver which provides an API for displaying on two
34+ 7-segment displays, and implements the required bit-banging.
35+ The hardware assumed is 74HC164 wired to two 7-segment displays.
36+
37 config PCI_ENDPOINT_TEST
38 depends on PCI
39 select CRC32
40# diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
41# index 402bcc3c9961..f39bec664d92 100644
42# --- a/drivers/misc/Makefile
43# +++ b/drivers/misc/Makefile
44# @@ -53,6 +53,8 @@ obj-$(CONFIG_GENWQE) += genwqe/
45# obj-$(CONFIG_ECHO) += echo/
46# obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
47# obj-$(CONFIG_CXL_BASE) += cxl/
48# +obj-$(CONFIG_SEVEN_SEGMENT_DISPLAY) += seven_seg_disp.o
49# +obj-$(CONFIG_SEVEN_SEGMENT_GPIO) += seven_seg_gpio.o
50# obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
51# obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
52# obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o
53
54diff --git a/drivers/misc/seven_seg_gpio.c b/drivers/misc/seven_seg_gpio.c
55new file mode 100644
56index 000000000000..e03ea7f8b848
57--- /dev/null
58+++ b/drivers/misc/seven_seg_gpio.c
59@@ -0,0 +1,205 @@
60+/*
61+ * Copyright (C) 2016 Google, Inc
62+ *
63+ * This program is free software; you can redistribute it and/or modify
64+ * it under the terms of the GNU General Public License version 2 or later as
65+ * published by the Free Software Foundation.
66+ */
67+
68+#include <linux/kernel.h>
69+#include <linux/module.h>
70+#include <linux/timer.h>
71+#include <linux/jiffies.h>
72+#include <linux/sizes.h>
73+#include <linux/io.h>
74+#include <linux/delay.h>
75+#include <linux/uaccess.h>
76+#include <linux/mutex.h>
77+#include <linux/of_platform.h>
78+#include <linux/gpio/consumer.h>
79+
80+#include "seven_seg_disp.h"
81+
82+#define DELAY_INTVL_US 1
83+
84+#define CLOCK_GPIO_NAME "clock"
85+#define DATA_GPIO_NAME "data"
86+#define CLEAR_GPIO_NAME "clear"
87+
88+struct seven_seg_gpio_info {
89+ u16 curr_disp_value;
90+ u16 refresh_interval;
91+ struct timer_list update_timer;
92+ struct gpio_desc *clock_gpio;
93+ struct gpio_desc *data_gpio;
94+ struct gpio_desc *clear_gpio;
95+};
96+
97+static void update_seven_seg_gpio_data(struct device *dev, u16 data)
98+{
99+ struct platform_device *pdev;
100+ struct seven_seg_gpio_info *gpio_info;
101+
102+ pdev = container_of(dev, struct platform_device, dev);
103+ if (pdev == NULL) {
104+ pr_err("invalid NULL platform_device\n");
105+ return;
106+ }
107+
108+ gpio_info = platform_get_drvdata(pdev);
109+ if (gpio_info == NULL) {
110+ pr_err("invalid NULL gpio_info\n");
111+ return;
112+ }
113+
114+ gpio_info->curr_disp_value = data;
115+}
116+
117+static void clear_seven_seg_gpio_data(struct device *dev, u16 data)
118+{
119+ struct platform_device *pdev;
120+ struct seven_seg_gpio_info *gpio_info;
121+
122+ pdev = container_of(dev, struct platform_device, dev);
123+ if (pdev == NULL) {
124+ pr_err("invalid NULL platform_device\n");
125+ return;
126+ }
127+
128+ gpio_info = platform_get_drvdata(pdev);
129+ if (gpio_info == NULL) {
130+ pr_err("invalid NULL gpio_info\n");
131+ return;
132+ }
133+
134+ gpio_info->curr_disp_value = 0;
135+}
136+
137+static void send_seven_seg_gpio_data(u16 disp_data,
138+ struct seven_seg_gpio_info *gpio_info)
139+{
140+ int i;
141+
142+ gpiod_set_value(gpio_info->clear_gpio, 0);
143+ udelay(DELAY_INTVL_US);
144+ gpiod_set_value(gpio_info->clear_gpio, 1);
145+ udelay(DELAY_INTVL_US);
146+
147+ for (i = 0; i < 16; i++) {
148+ if (disp_data & 0x01)
149+ gpiod_set_value(gpio_info->data_gpio, 1);
150+ else
151+ gpiod_set_value(gpio_info->data_gpio, 0);
152+
153+ udelay(DELAY_INTVL_US);
154+
155+ gpiod_set_value(gpio_info->clock_gpio, 0);
156+ udelay(DELAY_INTVL_US);
157+ gpiod_set_value(gpio_info->clock_gpio, 1);
158+ udelay(DELAY_INTVL_US);
159+
160+ disp_data >>= 1;
161+ }
162+}
163+
164+static void disp_refresh_timer_handler(struct timer_list *t)
165+{
166+ u16 disp_data;
167+ struct seven_seg_gpio_info *gpio_info =
168+ from_timer(gpio_info, t, update_timer);
169+ disp_data = gpio_info->curr_disp_value;
170+
171+ send_seven_seg_gpio_data(disp_data, gpio_info);
172+ mod_timer(&gpio_info->update_timer,
173+ jiffies + msecs_to_jiffies(gpio_info->refresh_interval));
174+}
175+
176+static const struct of_device_id of_seven_seg_gpio_match[] = {
177+ { .compatible = "seven-seg-gpio-dev" },
178+ {},
179+};
180+
181+MODULE_DEVICE_TABLE(of, of_seven_seg_gpio_match);
182+
183+static int seven_seg_gpio_probe(struct platform_device *pdev)
184+{
185+ u16 interval;
186+ int result;
187+ struct seven_seg_gpio_info *gpio_info;
188+ struct device *dev = &pdev->dev;
189+ struct seven_seg_disp_dev *disp_dev;
190+
191+ gpio_info = devm_kzalloc(dev,
192+ sizeof(struct seven_seg_gpio_info),
193+ GFP_KERNEL);
194+ if (gpio_info == NULL)
195+ return -ENOMEM;
196+
197+ /* Requesting the clock gpio */
198+ gpio_info->clock_gpio = devm_gpiod_get(dev, CLOCK_GPIO_NAME,
199+ GPIOD_OUT_HIGH);
200+ if (IS_ERR(gpio_info->clock_gpio))
201+ return PTR_ERR(gpio_info->clock_gpio);
202+
203+ /* Requesting the data gpio */
204+ gpio_info->data_gpio = devm_gpiod_get(dev, DATA_GPIO_NAME,
205+ GPIOD_OUT_HIGH);
206+ if (IS_ERR(gpio_info->data_gpio))
207+ return PTR_ERR(gpio_info->data_gpio);
208+
209+ /* Requesting the clear gpio */
210+ gpio_info->clear_gpio = devm_gpiod_get(dev, CLEAR_GPIO_NAME,
211+ GPIOD_OUT_HIGH);
212+ if (IS_ERR(gpio_info->clear_gpio))
213+ return PTR_ERR(gpio_info->clear_gpio);
214+
215+ result = of_property_read_u16(pdev->dev.of_node,
216+ "refresh-interval-ms", &interval);
217+ gpio_info->refresh_interval = result ? DEFAULT_REFRESH_INTERVAL_MS :
218+ interval;
219+
220+ /* Start timer to update seven segment display every second */
221+ timer_setup(&gpio_info->update_timer, disp_refresh_timer_handler, 0);
222+ result = mod_timer(&gpio_info->update_timer,
223+ jiffies +
224+ msecs_to_jiffies(gpio_info->refresh_interval));
225+ if (result)
226+ return result;
227+
228+ gpio_info->curr_disp_value = 0;
229+
230+ platform_set_drvdata(pdev, gpio_info);
231+
232+ disp_dev = devm_kzalloc(dev, sizeof(struct seven_seg_disp_dev),
233+ GFP_KERNEL);
234+ disp_dev->parent = *dev;
235+ seven_seg_setup_cdev(disp_dev, &update_seven_seg_gpio_data);
236+ return 0;
237+}
238+
239+static int seven_seg_gpio_remove(struct platform_device *pdev)
240+{
241+ struct seven_seg_gpio_info *gpio_info = platform_get_drvdata(pdev);
242+ struct seven_seg_disp_dev *disp_dev =
243+ container_of(&pdev->dev,
244+ struct seven_seg_disp_dev, parent);
245+ seven_seg_rem_cdev(disp_dev);
246+ del_timer_sync(&gpio_info->update_timer);
247+ platform_set_drvdata(pdev, NULL);
248+ return 0;
249+}
250+
251+static struct platform_driver seven_seg_gpio_driver = {
252+ .probe = seven_seg_gpio_probe,
253+ .remove = seven_seg_gpio_remove,
254+ .driver = {
255+ .name = "seven-seg-gpio",
256+ .of_match_table = of_seven_seg_gpio_match,
257+ },
258+};
259+
260+module_platform_driver(seven_seg_gpio_driver);
261+
262+MODULE_LICENSE("GPL");
263+MODULE_AUTHOR("Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>");
264+MODULE_DESCRIPTION("Seven segment display driver using GPIO config");
265--
2662.22.0.770.g0f2c4a37fd-goog
267