162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Buffalo Terastation Pro II/Live Board Setup 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Maintainer: Sylver Bruneau <sylver.bruneau@googlemail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/gpio.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/mtd/physmap.h> 1562306a36Sopenharmony_ci#include <linux/mv643xx_eth.h> 1662306a36Sopenharmony_ci#include <linux/i2c.h> 1762306a36Sopenharmony_ci#include <linux/serial_reg.h> 1862306a36Sopenharmony_ci#include <asm/mach-types.h> 1962306a36Sopenharmony_ci#include <asm/mach/arch.h> 2062306a36Sopenharmony_ci#include <asm/mach/pci.h> 2162306a36Sopenharmony_ci#include "common.h" 2262306a36Sopenharmony_ci#include "mpp.h" 2362306a36Sopenharmony_ci#include "orion5x.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/***************************************************************************** 2662306a36Sopenharmony_ci * Terastation Pro 2/Live Info 2762306a36Sopenharmony_ci ****************************************************************************/ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * Terastation Pro 2 hardware : 3162306a36Sopenharmony_ci * - Marvell 88F5281-D0 3262306a36Sopenharmony_ci * - Marvell 88SX6042 SATA controller (PCI) 3362306a36Sopenharmony_ci * - Marvell 88E1118 Gigabit Ethernet PHY 3462306a36Sopenharmony_ci * - 256KB NOR flash 3562306a36Sopenharmony_ci * - 128MB of DDR RAM 3662306a36Sopenharmony_ci * - PCIe port (not equipped) 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * 256K NOR flash Device bus boot chip select 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define TSP2_NOR_BOOT_BASE 0xf4000000 4462306a36Sopenharmony_ci#define TSP2_NOR_BOOT_SIZE SZ_256K 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/***************************************************************************** 4762306a36Sopenharmony_ci * 256KB NOR Flash on BOOT Device 4862306a36Sopenharmony_ci ****************************************************************************/ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic struct physmap_flash_data tsp2_nor_flash_data = { 5162306a36Sopenharmony_ci .width = 1, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic struct resource tsp2_nor_flash_resource = { 5562306a36Sopenharmony_ci .flags = IORESOURCE_MEM, 5662306a36Sopenharmony_ci .start = TSP2_NOR_BOOT_BASE, 5762306a36Sopenharmony_ci .end = TSP2_NOR_BOOT_BASE + TSP2_NOR_BOOT_SIZE - 1, 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic struct platform_device tsp2_nor_flash = { 6162306a36Sopenharmony_ci .name = "physmap-flash", 6262306a36Sopenharmony_ci .id = 0, 6362306a36Sopenharmony_ci .dev = { 6462306a36Sopenharmony_ci .platform_data = &tsp2_nor_flash_data, 6562306a36Sopenharmony_ci }, 6662306a36Sopenharmony_ci .num_resources = 1, 6762306a36Sopenharmony_ci .resource = &tsp2_nor_flash_resource, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/***************************************************************************** 7162306a36Sopenharmony_ci * PCI 7262306a36Sopenharmony_ci ****************************************************************************/ 7362306a36Sopenharmony_ci#define TSP2_PCI_SLOT0_OFFS 7 7462306a36Sopenharmony_ci#define TSP2_PCI_SLOT0_IRQ_PIN 11 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void __init tsp2_pci_preinit(void) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci int pin; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * Configure PCI GPIO IRQ pins 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci pin = TSP2_PCI_SLOT0_IRQ_PIN; 8462306a36Sopenharmony_ci if (gpio_request(pin, "PCI Int1") == 0) { 8562306a36Sopenharmony_ci if (gpio_direction_input(pin) == 0) { 8662306a36Sopenharmony_ci irq_set_irq_type(gpio_to_irq(pin), IRQ_TYPE_LEVEL_LOW); 8762306a36Sopenharmony_ci } else { 8862306a36Sopenharmony_ci printk(KERN_ERR "tsp2_pci_preinit failed " 8962306a36Sopenharmony_ci "to set_irq_type pin %d\n", pin); 9062306a36Sopenharmony_ci gpio_free(pin); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci } else { 9362306a36Sopenharmony_ci printk(KERN_ERR "tsp2_pci_preinit failed to " 9462306a36Sopenharmony_ci "gpio_request %d\n", pin); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int __init tsp2_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci int irq; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* 10362306a36Sopenharmony_ci * Check for devices with hard-wired IRQs. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci irq = orion5x_pci_map_irq(dev, slot, pin); 10662306a36Sopenharmony_ci if (irq != -1) 10762306a36Sopenharmony_ci return irq; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * PCI IRQs are connected via GPIOs. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci if (slot == TSP2_PCI_SLOT0_OFFS) 11362306a36Sopenharmony_ci return gpio_to_irq(TSP2_PCI_SLOT0_IRQ_PIN); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return -1; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic struct hw_pci tsp2_pci __initdata = { 11962306a36Sopenharmony_ci .nr_controllers = 2, 12062306a36Sopenharmony_ci .preinit = tsp2_pci_preinit, 12162306a36Sopenharmony_ci .setup = orion5x_pci_sys_setup, 12262306a36Sopenharmony_ci .scan = orion5x_pci_sys_scan_bus, 12362306a36Sopenharmony_ci .map_irq = tsp2_pci_map_irq, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int __init tsp2_pci_init(void) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci if (machine_is_terastation_pro2()) 12962306a36Sopenharmony_ci pci_common_init(&tsp2_pci); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cisubsys_initcall(tsp2_pci_init); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/***************************************************************************** 13762306a36Sopenharmony_ci * Ethernet 13862306a36Sopenharmony_ci ****************************************************************************/ 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic struct mv643xx_eth_platform_data tsp2_eth_data = { 14162306a36Sopenharmony_ci .phy_addr = 0, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/***************************************************************************** 14562306a36Sopenharmony_ci * RTC 5C372a on I2C bus 14662306a36Sopenharmony_ci ****************************************************************************/ 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci#define TSP2_RTC_GPIO 9 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic struct i2c_board_info __initdata tsp2_i2c_rtc = { 15162306a36Sopenharmony_ci I2C_BOARD_INFO("rs5c372a", 0x32), 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/***************************************************************************** 15562306a36Sopenharmony_ci * Terastation Pro II specific power off method via UART1-attached 15662306a36Sopenharmony_ci * microcontroller 15762306a36Sopenharmony_ci ****************************************************************************/ 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define UART1_REG(x) (UART1_VIRT_BASE + ((UART_##x) << 2)) 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int tsp2_miconread(unsigned char *buf, int count) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci int i; 16462306a36Sopenharmony_ci int timeout; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci for (i = 0; i < count; i++) { 16762306a36Sopenharmony_ci timeout = 10; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci while (!(readl(UART1_REG(LSR)) & UART_LSR_DR)) { 17062306a36Sopenharmony_ci if (--timeout == 0) 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci udelay(1000); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (timeout == 0) 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci buf[i] = readl(UART1_REG(RX)); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* return read bytes */ 18162306a36Sopenharmony_ci return i; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int tsp2_miconwrite(const unsigned char *buf, int count) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci int i = 0; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci while (count--) { 18962306a36Sopenharmony_ci while (!(readl(UART1_REG(LSR)) & UART_LSR_THRE)) 19062306a36Sopenharmony_ci barrier(); 19162306a36Sopenharmony_ci writel(buf[i++], UART1_REG(TX)); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int tsp2_miconsend(const unsigned char *data, int count) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int i; 20062306a36Sopenharmony_ci unsigned char checksum = 0; 20162306a36Sopenharmony_ci unsigned char recv_buf[40]; 20262306a36Sopenharmony_ci unsigned char send_buf[40]; 20362306a36Sopenharmony_ci unsigned char correct_ack[3]; 20462306a36Sopenharmony_ci int retry = 2; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* Generate checksum */ 20762306a36Sopenharmony_ci for (i = 0; i < count; i++) 20862306a36Sopenharmony_ci checksum -= data[i]; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci do { 21162306a36Sopenharmony_ci /* Send data */ 21262306a36Sopenharmony_ci tsp2_miconwrite(data, count); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* send checksum */ 21562306a36Sopenharmony_ci tsp2_miconwrite(&checksum, 1); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (tsp2_miconread(recv_buf, sizeof(recv_buf)) <= 3) { 21862306a36Sopenharmony_ci printk(KERN_ERR ">%s: receive failed.\n", __func__); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* send preamble to clear the receive buffer */ 22162306a36Sopenharmony_ci memset(&send_buf, 0xff, sizeof(send_buf)); 22262306a36Sopenharmony_ci tsp2_miconwrite(send_buf, sizeof(send_buf)); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* make dummy reads */ 22562306a36Sopenharmony_ci mdelay(100); 22662306a36Sopenharmony_ci tsp2_miconread(recv_buf, sizeof(recv_buf)); 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci /* Generate expected ack */ 22962306a36Sopenharmony_ci correct_ack[0] = 0x01; 23062306a36Sopenharmony_ci correct_ack[1] = data[1]; 23162306a36Sopenharmony_ci correct_ack[2] = 0x00; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* checksum Check */ 23462306a36Sopenharmony_ci if ((recv_buf[0] + recv_buf[1] + recv_buf[2] + 23562306a36Sopenharmony_ci recv_buf[3]) & 0xFF) { 23662306a36Sopenharmony_ci printk(KERN_ERR ">%s: Checksum Error : " 23762306a36Sopenharmony_ci "Received data[%02x, %02x, %02x, %02x]" 23862306a36Sopenharmony_ci "\n", __func__, recv_buf[0], 23962306a36Sopenharmony_ci recv_buf[1], recv_buf[2], recv_buf[3]); 24062306a36Sopenharmony_ci } else { 24162306a36Sopenharmony_ci /* Check Received Data */ 24262306a36Sopenharmony_ci if (correct_ack[0] == recv_buf[0] && 24362306a36Sopenharmony_ci correct_ack[1] == recv_buf[1] && 24462306a36Sopenharmony_ci correct_ack[2] == recv_buf[2]) { 24562306a36Sopenharmony_ci /* Interval for next command */ 24662306a36Sopenharmony_ci mdelay(10); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Receive ACK */ 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci /* Received NAK or illegal Data */ 25362306a36Sopenharmony_ci printk(KERN_ERR ">%s: Error : NAK or Illegal Data " 25462306a36Sopenharmony_ci "Received\n", __func__); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } while (retry--); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Interval for next command */ 25962306a36Sopenharmony_ci mdelay(10); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return -1; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void tsp2_power_off(void) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci const unsigned char watchdogkill[] = {0x01, 0x35, 0x00}; 26762306a36Sopenharmony_ci const unsigned char shutdownwait[] = {0x00, 0x0c}; 26862306a36Sopenharmony_ci const unsigned char poweroff[] = {0x00, 0x06}; 26962306a36Sopenharmony_ci /* 38400 baud divisor */ 27062306a36Sopenharmony_ci const unsigned divisor = ((orion5x_tclk + (8 * 38400)) / (16 * 38400)); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci pr_info("%s: triggering power-off...\n", __func__); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* hijack uart1 and reset into sane state (38400,8n1,even parity) */ 27562306a36Sopenharmony_ci writel(0x83, UART1_REG(LCR)); 27662306a36Sopenharmony_ci writel(divisor & 0xff, UART1_REG(DLL)); 27762306a36Sopenharmony_ci writel((divisor >> 8) & 0xff, UART1_REG(DLM)); 27862306a36Sopenharmony_ci writel(0x1b, UART1_REG(LCR)); 27962306a36Sopenharmony_ci writel(0x00, UART1_REG(IER)); 28062306a36Sopenharmony_ci writel(0x07, UART1_REG(FCR)); 28162306a36Sopenharmony_ci writel(0x00, UART1_REG(MCR)); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Send the commands to shutdown the Terastation Pro II */ 28462306a36Sopenharmony_ci tsp2_miconsend(watchdogkill, sizeof(watchdogkill)) ; 28562306a36Sopenharmony_ci tsp2_miconsend(shutdownwait, sizeof(shutdownwait)) ; 28662306a36Sopenharmony_ci tsp2_miconsend(poweroff, sizeof(poweroff)); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/***************************************************************************** 29062306a36Sopenharmony_ci * General Setup 29162306a36Sopenharmony_ci ****************************************************************************/ 29262306a36Sopenharmony_cistatic unsigned int tsp2_mpp_modes[] __initdata = { 29362306a36Sopenharmony_ci MPP0_PCIE_RST_OUTn, 29462306a36Sopenharmony_ci MPP1_UNUSED, 29562306a36Sopenharmony_ci MPP2_UNUSED, 29662306a36Sopenharmony_ci MPP3_UNUSED, 29762306a36Sopenharmony_ci MPP4_NAND, /* BOOT NAND Flash REn */ 29862306a36Sopenharmony_ci MPP5_NAND, /* BOOT NAND Flash WEn */ 29962306a36Sopenharmony_ci MPP6_NAND, /* BOOT NAND Flash HREn[0] */ 30062306a36Sopenharmony_ci MPP7_NAND, /* BOOT NAND Flash WEn[0] */ 30162306a36Sopenharmony_ci MPP8_GPIO, /* MICON int */ 30262306a36Sopenharmony_ci MPP9_GPIO, /* RTC int */ 30362306a36Sopenharmony_ci MPP10_UNUSED, 30462306a36Sopenharmony_ci MPP11_GPIO, /* PCI Int A */ 30562306a36Sopenharmony_ci MPP12_UNUSED, 30662306a36Sopenharmony_ci MPP13_GPIO, /* UPS on UART0 enable */ 30762306a36Sopenharmony_ci MPP14_GPIO, /* UPS low battery detection */ 30862306a36Sopenharmony_ci MPP15_UNUSED, 30962306a36Sopenharmony_ci MPP16_UART, /* UART1 RXD */ 31062306a36Sopenharmony_ci MPP17_UART, /* UART1 TXD */ 31162306a36Sopenharmony_ci MPP18_UART, /* UART1 CTSn */ 31262306a36Sopenharmony_ci MPP19_UART, /* UART1 RTSn */ 31362306a36Sopenharmony_ci 0, 31462306a36Sopenharmony_ci}; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic void __init tsp2_init(void) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci /* 31962306a36Sopenharmony_ci * Setup basic Orion functions. Need to be called early. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci orion5x_init(); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci orion5x_mpp_conf(tsp2_mpp_modes); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* 32662306a36Sopenharmony_ci * Configure peripherals. 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_ci mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET, 32962306a36Sopenharmony_ci ORION_MBUS_DEVBUS_BOOT_ATTR, 33062306a36Sopenharmony_ci TSP2_NOR_BOOT_BASE, 33162306a36Sopenharmony_ci TSP2_NOR_BOOT_SIZE); 33262306a36Sopenharmony_ci platform_device_register(&tsp2_nor_flash); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci orion5x_ehci0_init(); 33562306a36Sopenharmony_ci orion5x_eth_init(&tsp2_eth_data); 33662306a36Sopenharmony_ci orion5x_i2c_init(); 33762306a36Sopenharmony_ci orion5x_uart0_init(); 33862306a36Sopenharmony_ci orion5x_uart1_init(); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Get RTC IRQ and register the chip */ 34162306a36Sopenharmony_ci if (gpio_request(TSP2_RTC_GPIO, "rtc") == 0) { 34262306a36Sopenharmony_ci if (gpio_direction_input(TSP2_RTC_GPIO) == 0) 34362306a36Sopenharmony_ci tsp2_i2c_rtc.irq = gpio_to_irq(TSP2_RTC_GPIO); 34462306a36Sopenharmony_ci else 34562306a36Sopenharmony_ci gpio_free(TSP2_RTC_GPIO); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci if (tsp2_i2c_rtc.irq == 0) 34862306a36Sopenharmony_ci pr_warn("tsp2_init: failed to get RTC IRQ\n"); 34962306a36Sopenharmony_ci i2c_register_board_info(0, &tsp2_i2c_rtc, 1); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* register Terastation Pro II specific power-off method */ 35262306a36Sopenharmony_ci pm_power_off = tsp2_power_off; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ciMACHINE_START(TERASTATION_PRO2, "Buffalo Terastation Pro II/Live") 35662306a36Sopenharmony_ci /* Maintainer: Sylver Bruneau <sylver.bruneau@googlemail.com> */ 35762306a36Sopenharmony_ci .atag_offset = 0x100, 35862306a36Sopenharmony_ci .nr_irqs = ORION5X_NR_IRQS, 35962306a36Sopenharmony_ci .init_machine = tsp2_init, 36062306a36Sopenharmony_ci .map_io = orion5x_map_io, 36162306a36Sopenharmony_ci .init_early = orion5x_init_early, 36262306a36Sopenharmony_ci .init_irq = orion5x_init_irq, 36362306a36Sopenharmony_ci .init_time = orion5x_timer_init, 36462306a36Sopenharmony_ci .fixup = tag_fixup_mem32, 36562306a36Sopenharmony_ci .restart = orion5x_restart, 36662306a36Sopenharmony_ciMACHINE_END 367