1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2012-2015 Panasonic Corporation
4 * Copyright (C) 2015-2020 Socionext Inc.
5 *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
6 */
7
8#include <dm.h>
9#include <fdt_support.h>
10#include <linux/ctype.h>
11#include <linux/delay.h>
12#include <linux/io.h>
13#include <asm/global_data.h>
14
15#include "micro-support-card.h"
16
17#define SMC911X_OFFSET			0x00000
18#define LED_OFFSET			0x90000
19#define NS16550A_OFFSET			0xb0000
20#define MICRO_SUPPORT_CARD_RESET	0xd0034
21#define MICRO_SUPPORT_CARD_REVISION	0xd00e0
22
23static bool support_card_found;
24static void __iomem *support_card_base;
25
26static void support_card_detect(void)
27{
28	DECLARE_GLOBAL_DATA_PTR;
29	const void *fdt = gd->fdt_blob;
30	int offset;
31	u64 addr, addr2;
32
33	offset = fdt_node_offset_by_compatible(fdt, 0, "smsc,lan9118");
34	if (offset < 0)
35		return;
36
37	addr = fdt_get_base_address(fdt, offset);
38	if (addr == OF_BAD_ADDR)
39		return;
40	addr -= SMC911X_OFFSET;
41
42	offset = fdt_node_offset_by_compatible(fdt, 0, "ns16550a");
43	if (offset < 0)
44		return;
45
46	addr2 = fdt_get_base_address(fdt, offset);
47	if (addr2 == OF_BAD_ADDR)
48		return;
49	addr2 -= NS16550A_OFFSET;
50
51	/* sanity check */
52	if (addr != addr2)
53		return;
54
55	support_card_base = ioremap(addr, 0x100000);
56
57	support_card_found = true;
58}
59
60/*
61 * 0: reset deassert, 1: reset
62 *
63 * bit[0]: LAN, I2C, LED
64 * bit[1]: UART
65 */
66static void support_card_reset_deassert(void)
67{
68	writel(0x00010000, support_card_base + MICRO_SUPPORT_CARD_RESET);
69}
70
71static void support_card_reset(void)
72{
73	writel(0x00020003, support_card_base + MICRO_SUPPORT_CARD_RESET);
74}
75
76static int support_card_show_revision(void)
77{
78	u32 revision;
79
80	revision = readl(support_card_base + MICRO_SUPPORT_CARD_REVISION);
81	revision &= 0xff;
82
83	/* revision 3.6.x card changed the revision format */
84	printf("SC:    Micro Support Card (CPLD version %s%d.%d)\n",
85	       revision >> 4 == 6 ? "3." : "",
86	       revision >> 4, revision & 0xf);
87
88	return 0;
89}
90
91void support_card_init(void)
92{
93	struct udevice *dev;
94	int ret;
95
96	/* The system bus must be initialized for access to the support card. */
97	ret = uclass_get_device_by_driver(UCLASS_SIMPLE_BUS,
98					  DM_DRIVER_GET(uniphier_system_bus_driver),
99					  &dev);
100	if (ret)
101		return;
102
103	/* Check DT to see if this board has the support card. */
104	support_card_detect();
105
106	if (!support_card_found)
107		return;
108
109	support_card_reset();
110	/*
111	 * After power on, we need to keep the LAN controller in reset state
112	 * for a while. (200 usec)
113	 */
114	udelay(200);
115	support_card_reset_deassert();
116
117	support_card_show_revision();
118}
119
120static const u8 ledval_num[] = {
121	0x7e, /* 0 */
122	0x0c, /* 1 */
123	0xb6, /* 2 */
124	0x9e, /* 3 */
125	0xcc, /* 4 */
126	0xda, /* 5 */
127	0xfa, /* 6 */
128	0x4e, /* 7 */
129	0xfe, /* 8 */
130	0xde, /* 9 */
131};
132
133static const u8 ledval_alpha[] = {
134	0xee, /* A */
135	0xf8, /* B */
136	0x72, /* C */
137	0xbc, /* D */
138	0xf2, /* E */
139	0xe2, /* F */
140	0x7a, /* G */
141	0xe8, /* H */
142	0x08, /* I */
143	0x3c, /* J */
144	0xea, /* K */
145	0x70, /* L */
146	0x6e, /* M */
147	0xa8, /* N */
148	0xb8, /* O */
149	0xe6, /* P */
150	0xce, /* Q */
151	0xa0, /* R */
152	0xc8, /* S */
153	0x8c, /* T */
154	0x7c, /* U */
155	0x54, /* V */
156	0xfc, /* W */
157	0xec, /* X */
158	0xdc, /* Y */
159	0xa4, /* Z */
160};
161
162static u8 char2ledval(char c)
163{
164	if (isdigit(c))
165		return ledval_num[c - '0'];
166	else if (isalpha(c))
167		return ledval_alpha[toupper(c) - 'A'];
168
169	return 0;
170}
171
172void led_puts(const char *s)
173{
174	int i;
175	u32 val = 0;
176
177	if (!support_card_found)
178		return;
179
180	if (!s)
181		return;
182
183	for (i = 0; i < 4; i++) {
184		val <<= 8;
185		val |= char2ledval(*s);
186		if (*s != '\0')
187			s++;
188	}
189
190	writel(~val, support_card_base + LED_OFFSET);
191}
192