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