^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * QNAP TS-x09 Boards common functions
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Maintainers: Lennert Buytenhek <buytenh@marvell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Byron Bradley <byron.bbradley@gmail.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/pci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/mv643xx_eth.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/timex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/serial_reg.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include "orion5x.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include "tsx09-common.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include "common.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) /*****************************************************************************
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * QNAP TS-x09 specific power off method via UART1-attached PIC
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) ****************************************************************************/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define UART1_REG(x) (UART1_VIRT_BASE + ((UART_##x) << 2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) void qnap_tsx09_power_off(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) /* 19200 baud divisor */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) const unsigned divisor = ((orion5x_tclk + (8 * 19200)) / (16 * 19200));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) pr_info("%s: triggering power-off...\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) /* hijack uart1 and reset into sane state (19200,8n1) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) writel(0x83, UART1_REG(LCR));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) writel(divisor & 0xff, UART1_REG(DLL));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) writel((divisor >> 8) & 0xff, UART1_REG(DLM));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) writel(0x03, UART1_REG(LCR));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) writel(0x00, UART1_REG(IER));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) writel(0x00, UART1_REG(FCR));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) writel(0x00, UART1_REG(MCR));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) /* send the power-off command 'A' to PIC */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) writel('A', UART1_REG(TX));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) /*****************************************************************************
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * Ethernet
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) ****************************************************************************/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) struct mv643xx_eth_platform_data qnap_tsx09_eth_data = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) .phy_addr = MV643XX_ETH_PHY_ADDR(8),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) static int __init qnap_tsx09_parse_hex_nibble(char n)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) if (n >= '0' && n <= '9')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) return n - '0';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) if (n >= 'A' && n <= 'F')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) return n - 'A' + 10;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) if (n >= 'a' && n <= 'f')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) return n - 'a' + 10;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) static int __init qnap_tsx09_parse_hex_byte(const char *b)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) int hi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) int lo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) hi = qnap_tsx09_parse_hex_nibble(b[0]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) lo = qnap_tsx09_parse_hex_nibble(b[1]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) if (hi < 0 || lo < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) return (hi << 4) | lo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) static int __init qnap_tsx09_check_mac_addr(const char *addr_str)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) u_int8_t addr[6];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) for (i = 0; i < 6; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) int byte;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) * Enforce "xx:xx:xx:xx:xx:xx\n" format.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if (addr_str[(i * 3) + 2] != ((i < 5) ? ':' : '\n'))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) byte = qnap_tsx09_parse_hex_byte(addr_str + (i * 3));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (byte < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) addr[i] = byte;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) printk(KERN_INFO "tsx09: found ethernet mac address %pM\n", addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) memcpy(qnap_tsx09_eth_data.mac_addr, addr, 6);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) * The 'NAS Config' flash partition has an ext2 filesystem which
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) * contains a file that has the ethernet MAC address in plain text
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) * (format "xx:xx:xx:xx:xx:xx\n").
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) void __init qnap_tsx09_find_mac_addr(u32 mem_base, u32 size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) unsigned long addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) for (addr = mem_base; addr < (mem_base + size); addr += 1024) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) char *nor_page;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) nor_page = ioremap(addr, 1024);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) if (nor_page != NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) ret = qnap_tsx09_check_mac_addr(nor_page);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) iounmap(nor_page);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (ret == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }