diff mbox

[U-Boot,RESEND,8/9] sunxi: Add CHIP's DIP support

Message ID ad06e2d73a2af7f8e41053c168fb5d75a2b2a36b.1478600213.git-series.maxime.ripard@free-electrons.com
State Deferred
Delegated to: Tom Rini
Headers show

Commit Message

Maxime Ripard Nov. 8, 2016, 10:19 a.m. UTC
The NextThing CHIP comes with expansion boards called DIPs.

These DIPs comes with a 1-Wire EEPROM used to enumerate and identify the
DIPs currently attached.

Once we know what is connected, we need to do various things, such a load
and apply an overlay if relevant, adjust the U-boot environment and the
kernel command line, etc.

Add support for this.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 board/sunxi/Kconfig  |   9 ++-
 board/sunxi/Makefile |   1 +-
 board/sunxi/board.c  |   6 +-
 board/sunxi/dip.c    | 227 ++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 243 insertions(+), 0 deletions(-)
 create mode 100644 board/sunxi/dip.c
diff mbox

Patch

diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
index e1d4ab148f08..1e70f3a2ed97 100644
--- a/board/sunxi/Kconfig
+++ b/board/sunxi/Kconfig
@@ -667,4 +667,13 @@  config SPL_STACK_R_ADDR
 	default 0x4fe00000 if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I || MACH_SUN7I || MACH_SUN8I || MACH_SUN50I
 	default 0x2fe00000 if MACH_SUN9I
 
+config CHIP_DIP
+	bool "Enable NextThing's CHIP DIP support"
+	depends on W1_GPIO
+	depends on EEPROM_DS2431
+	---help---
+	The NextThing's CHIP allows to plug expansion boards. These boards can
+	be enumerated at runtime through a 1-Wire bus, each board having an
+	EEPROM connected to it, holding data to identify the board holding it.
+
 endif
diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile
index 43766e0ef482..0f0ac90ef185 100644
--- a/board/sunxi/Makefile
+++ b/board/sunxi/Makefile
@@ -9,6 +9,7 @@ 
 # SPDX-License-Identifier:	GPL-2.0+
 #
 obj-y	+= board.o
+obj-$(CONFIG_CHIP_DIP)		+= dip.o
 obj-$(CONFIG_SUNXI_GMAC)	+= gmac.o
 obj-$(CONFIG_SUNXI_AHCI)	+= ahci.o
 obj-$(CONFIG_MACH_SUN4I)	+= dram_sun4i_auto.o
diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 53656383d512..8cb7267b0a10 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -719,6 +719,12 @@  int ft_board_setup(void *blob, bd_t *bd)
 	 */
 	setup_environment(blob);
 
+#ifdef CONFIG_CHIP_DIP
+	r = chip_dip_dt_setup(blob);
+	if (r)
+		return r;
+#endif
+
 #ifdef CONFIG_VIDEO_DT_SIMPLEFB
 	r = sunxi_simplefb_setup(blob);
 	if (r)
diff --git a/board/sunxi/dip.c b/board/sunxi/dip.c
new file mode 100644
index 000000000000..af908917edfe
--- /dev/null
+++ b/board/sunxi/dip.c
@@ -0,0 +1,227 @@ 
+/*
+ * (C) Copyright 2016 NextThing Co
+ * (C) Copyright 2016 Free Electrons
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * CHIP's DIP spec implementation in U-Boot
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <eeprom.h>
+#include <w1.h>
+
+#include <asm/arch/gpio.h>
+
+#include <dm/device-internal.h>
+
+
+#define dip_convert(field)						\
+	(								\
+		(sizeof(field) == 1) ? field :				\
+		(sizeof(field) == 2) ? be16_to_cpu(field) :		\
+		(sizeof(field) == 4) ? be32_to_cpu(field) :		\
+		-1							\
+	)
+
+#define DIP_MAGIC	0x50494843	/* CHIP */
+
+#define DIP_VID_NTC		0x9d011a
+#define DIP_PID_NTC_POCKET	0x1
+#define DIP_PID_NTC_VGA		0x2
+#define DIP_PID_NTC_HDMI	0x3
+
+struct dip_header {
+	u32     magic;                  /* CHIP */
+	u8      version;                /* spec version */
+	u32     vendor_id;
+	u16     product_id;
+	u8      product_version;
+	char    vendor_name[32];
+	char    product_name[32];
+	u8      rsvd[36];               /* rsvd for futre spec versions */
+	u8      data[16];               /* user data, per-dip specific */
+} __packed;
+
+struct dip {
+	struct list_head	list;
+	char			file[64];
+};
+
+enum disp_output {
+	DISPLAY_COMPOSITE,
+	DISPLAY_RGB_HDMI_BRIDGE,
+	DISPLAY_RGB_VGA_BRIDGE,
+	DISPLAY_RGB_POCKET,
+};
+
+static LIST_HEAD(dip_list);
+
+static void dip_setup_pocket_display(enum disp_output display)
+{
+	char kernel[128];
+	char video[128];
+	char *s, *kmode;
+	int x, y;
+
+	s = getenv("dip-auto-video");
+	if (s && !strcmp(s, "no")) {
+		printf("DIP: User disabled auto setup. Aborting.\n");
+		return;
+	}
+
+	switch (display) {
+	case DISPLAY_RGB_HDMI_BRIDGE:
+		strncpy(kernel, "video=HDMI-A-1:1024x768@60", sizeof(kernel));
+		strncpy(video, "sunxi:1024x768-24@60,monitor=hdmi",
+			sizeof(video));
+		break;
+
+	case DISPLAY_RGB_VGA_BRIDGE:
+		strncpy(kernel, "video=VGA-1:1024x768@60", sizeof(kernel));
+		strncpy(video, "sunxi:1024x768-24@60,monitor=vga",
+			sizeof(video));
+		break;
+
+	case DISPLAY_RGB_POCKET:
+		strncpy(video, "sunxi:480x272-16@60,monitor=lcd",
+			sizeof(video));
+		break;
+
+	default:
+		s = getenv("tv-mode");
+		if (!s)
+			s = "ntsc";
+
+		if (!strcmp(s, "ntsc")) {
+			x = 720;
+			y = 480;
+			kmode = "NTSC";
+		} else if (!strcmp(s, "pal")) {
+			x = 720;
+			y = 576;
+			kmode = "PAL";
+		} else {
+			printf("DIP: Unknown TV format: %s\n", s);
+			return;
+		}
+
+		snprintf(kernel, sizeof(kernel), "video=Composite-1:%s",
+			 kmode);
+		snprintf(video, sizeof(video),
+			 "sunxi:%dx%d-24@60,monitor=composite-%s,overscan_x=40,overscan_y=20",
+			 x, y, s);
+
+		break;
+	}
+
+	setenv("kernelarg_video", kernel);
+	setenv("video-mode", video);
+}
+
+static void dip_detect(void)
+{
+	struct udevice *bus, *dev;
+	u8 display = DISPLAY_COMPOSITE;
+	u32 vid;
+	u16 pid;
+	int ret;
+
+	sunxi_gpio_set_pull(SUNXI_GPD(2), SUNXI_GPIO_PULL_UP);
+
+	w1_get_bus(0, &bus);
+
+	for (device_find_first_child(bus, &dev); dev;
+	     device_find_next_child(&dev)) {
+		struct dip_header header;
+		struct dip *dip;
+
+		if (w1_get_device_family(dev) != W1_FAMILY_DS2431)
+			continue;
+
+		ret = device_probe(dev);
+		if (ret) {
+			printf("Couldn't probe device %s: error %d",
+			       dev->name, ret);
+			continue;
+		}
+
+		eeprom_read_buf(dev, 0, (u8 *)&header, sizeof(header));
+
+		if (header.magic != DIP_MAGIC)
+			continue;
+
+		vid = dip_convert(header.vendor_id);
+		pid = dip_convert(header.product_id);
+
+		printf("DIP: Found %s (0x%x) from %s (0x%x) detected\n",
+		       header.product_name, pid,
+		       header.vendor_name, vid);
+
+		dip = calloc(sizeof(*dip), 1);
+		if (!dip)
+			return;
+
+		snprintf(dip->file, sizeof(dip->file), "dip-%x-%x.dtbo",
+			 vid, pid);
+		list_add_tail(&dip->list, &dip_list);
+
+		if (vid == DIP_VID_NTC) {
+			switch (pid) {
+			case DIP_PID_NTC_POCKET:
+				display = DISPLAY_RGB_POCKET;
+				break;
+
+			case DIP_PID_NTC_HDMI:
+				display = DISPLAY_RGB_HDMI_BRIDGE;
+				break;
+
+			case DIP_PID_NTC_VGA:
+				display = DISPLAY_RGB_VGA_BRIDGE;
+				break;
+			}
+		}
+	}
+
+	dip_setup_pocket_display(display);
+}
+
+int board_video_pre_init(void)
+{
+	dip_detect();
+
+	return 0;
+}
+
+int chip_dip_dt_setup(void)
+{
+	struct dip *dip, *next;
+	int ret;
+	char *cmd;
+
+	cmd = getenv("dip_overlay_cmd");
+	if (!cmd)
+		return 0;
+
+	list_for_each_entry_safe(dip, next, &dip_list, list) {
+		printf("DIP: Applying dip overlay %s\n", dip->file);
+		setenv("dip_overlay_name", dip->file);
+		ret = run_command(cmd, 0);
+
+		/* First remove the item from the list */
+		list_del(&dip->list);
+		free(dip);
+
+		/* And then check if there was an error */
+		if (ret)
+			continue;
+
+		ret = run_command("fdt apply $dip_addr_r", 0);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}