Tom Rini | 83d290c | 2018-05-06 17:58:06 -0400 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
Phil Sutter | a12d3e4 | 2015-12-25 14:41:26 +0100 | [diff] [blame] | 2 | /* |
| 3 | * Commands to deal with Synology specifics. |
| 4 | * |
| 5 | * Copyright (C) 2015 Phil Sutter <phil@nwl.cc> |
Phil Sutter | a12d3e4 | 2015-12-25 14:41:26 +0100 | [diff] [blame] | 6 | */ |
| 7 | |
| 8 | #include <common.h> |
| 9 | #include <div64.h> |
| 10 | #include <spi.h> |
| 11 | #include <spi_flash.h> |
| 12 | #include <linux/mtd/mtd.h> |
| 13 | |
| 14 | #include <asm/io.h> |
| 15 | #include "../drivers/ddr/marvell/axp/ddr3_init.h" |
| 16 | |
| 17 | #define ETH_ALEN 6 |
| 18 | #define ETHADDR_MAX 4 |
| 19 | #define SYNO_SN_TAG "SN=" |
| 20 | #define SYNO_CHKSUM_TAG "CHK=" |
| 21 | |
| 22 | |
| 23 | static int do_syno_populate(int argc, char * const argv[]) |
| 24 | { |
| 25 | unsigned int bus = CONFIG_SF_DEFAULT_BUS; |
| 26 | unsigned int cs = CONFIG_SF_DEFAULT_CS; |
| 27 | unsigned int speed = CONFIG_SF_DEFAULT_SPEED; |
| 28 | unsigned int mode = CONFIG_SF_DEFAULT_MODE; |
| 29 | struct spi_flash *flash; |
| 30 | unsigned long addr = 0x80000; /* XXX: parameterize this? */ |
| 31 | loff_t offset = 0x007d0000; |
| 32 | loff_t len = 0x00010000; |
| 33 | char *buf, *bufp; |
| 34 | char var[128]; |
| 35 | char val[128]; |
| 36 | int ret, n; |
| 37 | |
| 38 | /* XXX: arg parsing to select flash here? */ |
| 39 | |
| 40 | flash = spi_flash_probe(bus, cs, speed, mode); |
| 41 | if (!flash) { |
| 42 | printf("Failed to initialize SPI flash at %u:%u\n", bus, cs); |
| 43 | return 1; |
| 44 | } |
| 45 | |
| 46 | buf = map_physmem(addr, len, MAP_WRBACK); |
| 47 | if (!buf) { |
| 48 | puts("Failed to map physical memory\n"); |
| 49 | return 1; |
| 50 | } |
| 51 | |
| 52 | ret = spi_flash_read(flash, offset, len, buf); |
| 53 | if (ret) { |
| 54 | puts("Failed to read from SPI flash\n"); |
| 55 | goto out_unmap; |
| 56 | } |
| 57 | |
| 58 | for (n = 0; n < ETHADDR_MAX; n++) { |
| 59 | char ethaddr[ETH_ALEN]; |
| 60 | int i, sum = 0; |
| 61 | unsigned char csum = 0; |
| 62 | |
| 63 | for (i = 0, bufp = buf + n * 7; i < ETH_ALEN; i++) { |
| 64 | sum += bufp[i]; |
| 65 | csum += bufp[i]; |
| 66 | ethaddr[i] = bufp[i]; |
| 67 | } |
| 68 | if (!sum) /* MAC address empty */ |
| 69 | continue; |
| 70 | if (csum != bufp[i]) { /* seventh byte is checksum value */ |
| 71 | printf("Invalid MAC address for interface %d!\n", n); |
| 72 | continue; |
| 73 | } |
| 74 | if (n == 0) |
| 75 | sprintf(var, "ethaddr"); |
| 76 | else |
| 77 | sprintf(var, "eth%daddr", n); |
| 78 | snprintf(val, sizeof(val) - 1, |
| 79 | "%02x:%02x:%02x:%02x:%02x:%02x", |
| 80 | ethaddr[0], ethaddr[1], ethaddr[2], |
| 81 | ethaddr[3], ethaddr[4], ethaddr[5]); |
| 82 | printf("parsed %s = %s\n", var, val); |
Simon Glass | 382bee5 | 2017-08-03 12:22:09 -0600 | [diff] [blame] | 83 | env_set(var, val); |
Phil Sutter | a12d3e4 | 2015-12-25 14:41:26 +0100 | [diff] [blame] | 84 | } |
| 85 | if (!strncmp(buf + 32, SYNO_SN_TAG, strlen(SYNO_SN_TAG))) { |
| 86 | char *snp, *csump; |
| 87 | int csum = 0; |
| 88 | unsigned long c; |
| 89 | |
| 90 | snp = bufp = buf + 32 + strlen(SYNO_SN_TAG); |
| 91 | for (n = 0; bufp[n] && bufp[n] != ','; n++) |
| 92 | csum += bufp[n]; |
| 93 | bufp[n] = '\0'; |
| 94 | |
| 95 | /* should come right after, but you never know */ |
| 96 | bufp = strstr(bufp + n + 1, SYNO_CHKSUM_TAG); |
| 97 | if (!bufp) { |
| 98 | printf("Serial number checksum tag missing!\n"); |
| 99 | goto out_unmap; |
| 100 | } |
| 101 | |
| 102 | csump = bufp += strlen(SYNO_CHKSUM_TAG); |
| 103 | for (n = 0; bufp[n] && bufp[n] != ','; n++) |
| 104 | ; |
| 105 | bufp[n] = '\0'; |
| 106 | |
| 107 | if (strict_strtoul(csump, 10, &c) || c != csum) { |
| 108 | puts("Invalid serial number found!\n"); |
| 109 | ret = 1; |
| 110 | goto out_unmap; |
| 111 | } |
| 112 | printf("parsed SN = %s\n", snp); |
Simon Glass | 382bee5 | 2017-08-03 12:22:09 -0600 | [diff] [blame] | 113 | env_set("SN", snp); |
Phil Sutter | a12d3e4 | 2015-12-25 14:41:26 +0100 | [diff] [blame] | 114 | } else { /* old style format */ |
| 115 | unsigned char csum = 0; |
| 116 | |
| 117 | for (n = 0, bufp = buf + 32; n < 10; n++) |
| 118 | csum += bufp[n]; |
| 119 | |
| 120 | if (csum != bufp[n]) { |
| 121 | puts("Invalid serial number found!\n"); |
| 122 | ret = 1; |
| 123 | goto out_unmap; |
| 124 | } |
| 125 | bufp[n] = '\0'; |
| 126 | printf("parsed SN = %s\n", buf + 32); |
Simon Glass | 382bee5 | 2017-08-03 12:22:09 -0600 | [diff] [blame] | 127 | env_set("SN", buf + 32); |
Phil Sutter | a12d3e4 | 2015-12-25 14:41:26 +0100 | [diff] [blame] | 128 | } |
| 129 | out_unmap: |
| 130 | unmap_physmem(buf, len); |
| 131 | return ret; |
| 132 | } |
| 133 | |
| 134 | /* map bit position to function in POWER_MNG_CTRL_REG */ |
| 135 | static const char * const pwr_mng_bit_func[] = { |
| 136 | "audio", |
| 137 | "ge3", "ge2", "ge1", "ge0", |
| 138 | "pcie00", "pcie01", "pcie02", "pcie03", |
| 139 | "pcie10", "pcie11", "pcie12", "pcie13", |
| 140 | "bp", |
| 141 | "sata0_link", "sata0_core", |
| 142 | "lcd", |
| 143 | "sdio", |
| 144 | "usb0", "usb1", "usb2", |
| 145 | "idma", "xor0", "crypto", |
| 146 | NULL, |
| 147 | "tdm", |
| 148 | "pcie20", "pcie30", |
| 149 | "xor1", |
| 150 | "sata1_link", "sata1_core", |
| 151 | NULL, |
| 152 | }; |
| 153 | |
| 154 | static int do_syno_clk_gate(int argc, char * const argv[]) |
| 155 | { |
| 156 | u32 pwr_mng_ctrl_reg = reg_read(POWER_MNG_CTRL_REG); |
| 157 | const char *func, *state; |
| 158 | int i, val; |
| 159 | |
| 160 | if (argc < 2) |
| 161 | return -1; |
| 162 | |
| 163 | if (!strcmp(argv[1], "get")) { |
| 164 | puts("Clock Gating:\n"); |
| 165 | for (i = 0; i < 32; i++) { |
| 166 | func = pwr_mng_bit_func[i]; |
| 167 | if (!func) |
| 168 | continue; |
| 169 | state = pwr_mng_ctrl_reg & (1 << i) ? "ON" : "OFF"; |
| 170 | printf("%s:\t\t%s\n", func, state); |
| 171 | } |
| 172 | return 0; |
| 173 | } |
| 174 | if (argc < 4) |
| 175 | return -1; |
| 176 | if (!strcmp(argv[1], "set")) { |
| 177 | func = argv[2]; |
| 178 | state = argv[3]; |
| 179 | for (i = 0; i < 32; i++) { |
| 180 | if (!pwr_mng_bit_func[i]) |
| 181 | continue; |
| 182 | if (!strcmp(func, pwr_mng_bit_func[i])) |
| 183 | break; |
| 184 | } |
| 185 | if (i == 32) { |
| 186 | printf("Error: name '%s' not known\n", func); |
| 187 | return -1; |
| 188 | } |
| 189 | val = state[0] != '0'; |
| 190 | pwr_mng_ctrl_reg |= (val << i); |
| 191 | pwr_mng_ctrl_reg &= ~(!val << i); |
| 192 | reg_write(POWER_MNG_CTRL_REG, pwr_mng_ctrl_reg); |
| 193 | } |
| 194 | return 0; |
| 195 | } |
| 196 | |
| 197 | static int do_syno(cmd_tbl_t *cmdtp, int flag, |
| 198 | int argc, char * const argv[]) |
| 199 | { |
| 200 | const char *cmd; |
| 201 | int ret = 0; |
| 202 | |
| 203 | if (argc < 2) |
| 204 | goto usage; |
| 205 | |
| 206 | cmd = argv[1]; |
| 207 | --argc; |
| 208 | ++argv; |
| 209 | |
| 210 | if (!strcmp(cmd, "populate_env")) |
| 211 | ret = do_syno_populate(argc, argv); |
| 212 | else if (!strcmp(cmd, "clk_gate")) |
| 213 | ret = do_syno_clk_gate(argc, argv); |
| 214 | |
| 215 | if (ret != -1) |
| 216 | return ret; |
| 217 | usage: |
| 218 | return CMD_RET_USAGE; |
| 219 | } |
| 220 | |
| 221 | U_BOOT_CMD( |
| 222 | syno, 5, 1, do_syno, |
| 223 | "Synology specific commands", |
| 224 | "populate_env - Read vendor data from SPI flash into environment\n" |
| 225 | "clk_gate (get|set name 1|0) - Manage clock gating\n" |
| 226 | ); |