mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
574 lines
14 KiB
574 lines
14 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* arch/arm/mach-orion5x/ts78xx-setup.c |
|
* |
|
* Maintainer: Alexander Clouter <[email protected]> |
|
*/ |
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|
|
|
#include <linux/kernel.h> |
|
#include <linux/init.h> |
|
#include <linux/sysfs.h> |
|
#include <linux/platform_device.h> |
|
#include <linux/mv643xx_eth.h> |
|
#include <linux/ata_platform.h> |
|
#include <linux/mtd/platnand.h> |
|
#include <linux/timeriomem-rng.h> |
|
#include <asm/mach-types.h> |
|
#include <asm/mach/arch.h> |
|
#include <asm/mach/map.h> |
|
#include "common.h" |
|
#include "mpp.h" |
|
#include "orion5x.h" |
|
#include "ts78xx-fpga.h" |
|
|
|
/***************************************************************************** |
|
* TS-78xx Info |
|
****************************************************************************/ |
|
|
|
/* |
|
* FPGA - lives where the PCI bus would be at ORION5X_PCI_MEM_PHYS_BASE |
|
*/ |
|
#define TS78XX_FPGA_REGS_PHYS_BASE 0xe8000000 |
|
#define TS78XX_FPGA_REGS_VIRT_BASE IOMEM(0xff900000) |
|
#define TS78XX_FPGA_REGS_SIZE SZ_1M |
|
|
|
static struct ts78xx_fpga_data ts78xx_fpga = { |
|
.id = 0, |
|
.state = 1, |
|
/* .supports = ... - populated by ts78xx_fpga_supports() */ |
|
}; |
|
|
|
/***************************************************************************** |
|
* I/O Address Mapping |
|
****************************************************************************/ |
|
static struct map_desc ts78xx_io_desc[] __initdata = { |
|
{ |
|
.virtual = (unsigned long)TS78XX_FPGA_REGS_VIRT_BASE, |
|
.pfn = __phys_to_pfn(TS78XX_FPGA_REGS_PHYS_BASE), |
|
.length = TS78XX_FPGA_REGS_SIZE, |
|
.type = MT_DEVICE, |
|
}, |
|
}; |
|
|
|
static void __init ts78xx_map_io(void) |
|
{ |
|
orion5x_map_io(); |
|
iotable_init(ts78xx_io_desc, ARRAY_SIZE(ts78xx_io_desc)); |
|
} |
|
|
|
/***************************************************************************** |
|
* Ethernet |
|
****************************************************************************/ |
|
static struct mv643xx_eth_platform_data ts78xx_eth_data = { |
|
.phy_addr = MV643XX_ETH_PHY_ADDR(0), |
|
}; |
|
|
|
/***************************************************************************** |
|
* SATA |
|
****************************************************************************/ |
|
static struct mv_sata_platform_data ts78xx_sata_data = { |
|
.n_ports = 2, |
|
}; |
|
|
|
/***************************************************************************** |
|
* RTC M48T86 - nicked^Wborrowed from arch/arm/mach-ep93xx/ts72xx.c |
|
****************************************************************************/ |
|
#define TS_RTC_CTRL (TS78XX_FPGA_REGS_PHYS_BASE + 0x808) |
|
#define TS_RTC_DATA (TS78XX_FPGA_REGS_PHYS_BASE + 0x80c) |
|
|
|
static struct resource ts78xx_ts_rtc_resources[] = { |
|
DEFINE_RES_MEM(TS_RTC_CTRL, 0x01), |
|
DEFINE_RES_MEM(TS_RTC_DATA, 0x01), |
|
}; |
|
|
|
static struct platform_device ts78xx_ts_rtc_device = { |
|
.name = "rtc-m48t86", |
|
.id = -1, |
|
.resource = ts78xx_ts_rtc_resources, |
|
.num_resources = ARRAY_SIZE(ts78xx_ts_rtc_resources), |
|
}; |
|
|
|
static int ts78xx_ts_rtc_load(void) |
|
{ |
|
int rc; |
|
|
|
if (ts78xx_fpga.supports.ts_rtc.init == 0) { |
|
rc = platform_device_register(&ts78xx_ts_rtc_device); |
|
if (!rc) |
|
ts78xx_fpga.supports.ts_rtc.init = 1; |
|
} else { |
|
rc = platform_device_add(&ts78xx_ts_rtc_device); |
|
} |
|
|
|
if (rc) |
|
pr_info("RTC could not be registered: %d\n", rc); |
|
|
|
return rc; |
|
} |
|
|
|
static void ts78xx_ts_rtc_unload(void) |
|
{ |
|
platform_device_del(&ts78xx_ts_rtc_device); |
|
} |
|
|
|
/***************************************************************************** |
|
* NAND Flash |
|
****************************************************************************/ |
|
#define TS_NAND_CTRL (TS78XX_FPGA_REGS_VIRT_BASE + 0x800) /* VIRT */ |
|
#define TS_NAND_DATA (TS78XX_FPGA_REGS_PHYS_BASE + 0x804) /* PHYS */ |
|
|
|
/* |
|
* hardware specific access to control-lines |
|
* |
|
* ctrl: |
|
* NAND_NCE: bit 0 -> bit 2 |
|
* NAND_CLE: bit 1 -> bit 1 |
|
* NAND_ALE: bit 2 -> bit 0 |
|
*/ |
|
static void ts78xx_ts_nand_cmd_ctrl(struct nand_chip *this, int cmd, |
|
unsigned int ctrl) |
|
{ |
|
if (ctrl & NAND_CTRL_CHANGE) { |
|
unsigned char bits; |
|
|
|
bits = (ctrl & NAND_NCE) << 2; |
|
bits |= ctrl & NAND_CLE; |
|
bits |= (ctrl & NAND_ALE) >> 2; |
|
|
|
writeb((readb(TS_NAND_CTRL) & ~0x7) | bits, TS_NAND_CTRL); |
|
} |
|
|
|
if (cmd != NAND_CMD_NONE) |
|
writeb(cmd, this->legacy.IO_ADDR_W); |
|
} |
|
|
|
static int ts78xx_ts_nand_dev_ready(struct nand_chip *chip) |
|
{ |
|
return readb(TS_NAND_CTRL) & 0x20; |
|
} |
|
|
|
static void ts78xx_ts_nand_write_buf(struct nand_chip *chip, |
|
const uint8_t *buf, int len) |
|
{ |
|
void __iomem *io_base = chip->legacy.IO_ADDR_W; |
|
unsigned long off = ((unsigned long)buf & 3); |
|
int sz; |
|
|
|
if (off) { |
|
sz = min_t(int, 4 - off, len); |
|
writesb(io_base, buf, sz); |
|
buf += sz; |
|
len -= sz; |
|
} |
|
|
|
sz = len >> 2; |
|
if (sz) { |
|
u32 *buf32 = (u32 *)buf; |
|
writesl(io_base, buf32, sz); |
|
buf += sz << 2; |
|
len -= sz << 2; |
|
} |
|
|
|
if (len) |
|
writesb(io_base, buf, len); |
|
} |
|
|
|
static void ts78xx_ts_nand_read_buf(struct nand_chip *chip, |
|
uint8_t *buf, int len) |
|
{ |
|
void __iomem *io_base = chip->legacy.IO_ADDR_R; |
|
unsigned long off = ((unsigned long)buf & 3); |
|
int sz; |
|
|
|
if (off) { |
|
sz = min_t(int, 4 - off, len); |
|
readsb(io_base, buf, sz); |
|
buf += sz; |
|
len -= sz; |
|
} |
|
|
|
sz = len >> 2; |
|
if (sz) { |
|
u32 *buf32 = (u32 *)buf; |
|
readsl(io_base, buf32, sz); |
|
buf += sz << 2; |
|
len -= sz << 2; |
|
} |
|
|
|
if (len) |
|
readsb(io_base, buf, len); |
|
} |
|
|
|
static struct mtd_partition ts78xx_ts_nand_parts[] = { |
|
{ |
|
.name = "mbr", |
|
.offset = 0, |
|
.size = SZ_128K, |
|
.mask_flags = MTD_WRITEABLE, |
|
}, { |
|
.name = "kernel", |
|
.offset = MTDPART_OFS_APPEND, |
|
.size = SZ_4M, |
|
}, { |
|
.name = "initrd", |
|
.offset = MTDPART_OFS_APPEND, |
|
.size = SZ_4M, |
|
}, { |
|
.name = "rootfs", |
|
.offset = MTDPART_OFS_APPEND, |
|
.size = MTDPART_SIZ_FULL, |
|
} |
|
}; |
|
|
|
static struct platform_nand_data ts78xx_ts_nand_data = { |
|
.chip = { |
|
.nr_chips = 1, |
|
.partitions = ts78xx_ts_nand_parts, |
|
.nr_partitions = ARRAY_SIZE(ts78xx_ts_nand_parts), |
|
.chip_delay = 15, |
|
.bbt_options = NAND_BBT_USE_FLASH, |
|
}, |
|
.ctrl = { |
|
/* |
|
* The HW ECC offloading functions, used to give about a 9% |
|
* performance increase for 'dd if=/dev/mtdblockX' and 5% for |
|
* nanddump. This all however was changed by git commit |
|
* e6cf5df1838c28bb060ac45b5585e48e71bbc740 so now there is |
|
* no performance advantage to be had so we no longer bother |
|
*/ |
|
.cmd_ctrl = ts78xx_ts_nand_cmd_ctrl, |
|
.dev_ready = ts78xx_ts_nand_dev_ready, |
|
.write_buf = ts78xx_ts_nand_write_buf, |
|
.read_buf = ts78xx_ts_nand_read_buf, |
|
}, |
|
}; |
|
|
|
static struct resource ts78xx_ts_nand_resources |
|
= DEFINE_RES_MEM(TS_NAND_DATA, 4); |
|
|
|
static struct platform_device ts78xx_ts_nand_device = { |
|
.name = "gen_nand", |
|
.id = -1, |
|
.dev = { |
|
.platform_data = &ts78xx_ts_nand_data, |
|
}, |
|
.resource = &ts78xx_ts_nand_resources, |
|
.num_resources = 1, |
|
}; |
|
|
|
static int ts78xx_ts_nand_load(void) |
|
{ |
|
int rc; |
|
|
|
if (ts78xx_fpga.supports.ts_nand.init == 0) { |
|
rc = platform_device_register(&ts78xx_ts_nand_device); |
|
if (!rc) |
|
ts78xx_fpga.supports.ts_nand.init = 1; |
|
} else |
|
rc = platform_device_add(&ts78xx_ts_nand_device); |
|
|
|
if (rc) |
|
pr_info("NAND could not be registered: %d\n", rc); |
|
return rc; |
|
}; |
|
|
|
static void ts78xx_ts_nand_unload(void) |
|
{ |
|
platform_device_del(&ts78xx_ts_nand_device); |
|
} |
|
|
|
/***************************************************************************** |
|
* HW RNG |
|
****************************************************************************/ |
|
#define TS_RNG_DATA (TS78XX_FPGA_REGS_PHYS_BASE | 0x044) |
|
|
|
static struct resource ts78xx_ts_rng_resource |
|
= DEFINE_RES_MEM(TS_RNG_DATA, 4); |
|
|
|
static struct timeriomem_rng_data ts78xx_ts_rng_data = { |
|
.period = 1000000, /* one second */ |
|
}; |
|
|
|
static struct platform_device ts78xx_ts_rng_device = { |
|
.name = "timeriomem_rng", |
|
.id = -1, |
|
.dev = { |
|
.platform_data = &ts78xx_ts_rng_data, |
|
}, |
|
.resource = &ts78xx_ts_rng_resource, |
|
.num_resources = 1, |
|
}; |
|
|
|
static int ts78xx_ts_rng_load(void) |
|
{ |
|
int rc; |
|
|
|
if (ts78xx_fpga.supports.ts_rng.init == 0) { |
|
rc = platform_device_register(&ts78xx_ts_rng_device); |
|
if (!rc) |
|
ts78xx_fpga.supports.ts_rng.init = 1; |
|
} else |
|
rc = platform_device_add(&ts78xx_ts_rng_device); |
|
|
|
if (rc) |
|
pr_info("RNG could not be registered: %d\n", rc); |
|
return rc; |
|
}; |
|
|
|
static void ts78xx_ts_rng_unload(void) |
|
{ |
|
platform_device_del(&ts78xx_ts_rng_device); |
|
} |
|
|
|
/***************************************************************************** |
|
* FPGA 'hotplug' support code |
|
****************************************************************************/ |
|
static void ts78xx_fpga_devices_zero_init(void) |
|
{ |
|
ts78xx_fpga.supports.ts_rtc.init = 0; |
|
ts78xx_fpga.supports.ts_nand.init = 0; |
|
ts78xx_fpga.supports.ts_rng.init = 0; |
|
} |
|
|
|
static void ts78xx_fpga_supports(void) |
|
{ |
|
/* TODO: put this 'table' into ts78xx-fpga.h */ |
|
switch (ts78xx_fpga.id) { |
|
case TS7800_REV_1: |
|
case TS7800_REV_2: |
|
case TS7800_REV_3: |
|
case TS7800_REV_4: |
|
case TS7800_REV_5: |
|
case TS7800_REV_6: |
|
case TS7800_REV_7: |
|
case TS7800_REV_8: |
|
case TS7800_REV_9: |
|
ts78xx_fpga.supports.ts_rtc.present = 1; |
|
ts78xx_fpga.supports.ts_nand.present = 1; |
|
ts78xx_fpga.supports.ts_rng.present = 1; |
|
break; |
|
default: |
|
/* enable devices if magic matches */ |
|
switch ((ts78xx_fpga.id >> 8) & 0xffffff) { |
|
case TS7800_FPGA_MAGIC: |
|
pr_warn("unrecognised FPGA revision 0x%.2x\n", |
|
ts78xx_fpga.id & 0xff); |
|
ts78xx_fpga.supports.ts_rtc.present = 1; |
|
ts78xx_fpga.supports.ts_nand.present = 1; |
|
ts78xx_fpga.supports.ts_rng.present = 1; |
|
break; |
|
default: |
|
ts78xx_fpga.supports.ts_rtc.present = 0; |
|
ts78xx_fpga.supports.ts_nand.present = 0; |
|
ts78xx_fpga.supports.ts_rng.present = 0; |
|
} |
|
} |
|
} |
|
|
|
static int ts78xx_fpga_load_devices(void) |
|
{ |
|
int tmp, ret = 0; |
|
|
|
if (ts78xx_fpga.supports.ts_rtc.present == 1) { |
|
tmp = ts78xx_ts_rtc_load(); |
|
if (tmp) |
|
ts78xx_fpga.supports.ts_rtc.present = 0; |
|
ret |= tmp; |
|
} |
|
if (ts78xx_fpga.supports.ts_nand.present == 1) { |
|
tmp = ts78xx_ts_nand_load(); |
|
if (tmp) |
|
ts78xx_fpga.supports.ts_nand.present = 0; |
|
ret |= tmp; |
|
} |
|
if (ts78xx_fpga.supports.ts_rng.present == 1) { |
|
tmp = ts78xx_ts_rng_load(); |
|
if (tmp) |
|
ts78xx_fpga.supports.ts_rng.present = 0; |
|
ret |= tmp; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
static int ts78xx_fpga_unload_devices(void) |
|
{ |
|
|
|
if (ts78xx_fpga.supports.ts_rtc.present == 1) |
|
ts78xx_ts_rtc_unload(); |
|
if (ts78xx_fpga.supports.ts_nand.present == 1) |
|
ts78xx_ts_nand_unload(); |
|
if (ts78xx_fpga.supports.ts_rng.present == 1) |
|
ts78xx_ts_rng_unload(); |
|
|
|
return 0; |
|
} |
|
|
|
static int ts78xx_fpga_load(void) |
|
{ |
|
ts78xx_fpga.id = readl(TS78XX_FPGA_REGS_VIRT_BASE); |
|
|
|
pr_info("FPGA magic=0x%.6x, rev=0x%.2x\n", |
|
(ts78xx_fpga.id >> 8) & 0xffffff, |
|
ts78xx_fpga.id & 0xff); |
|
|
|
ts78xx_fpga_supports(); |
|
|
|
if (ts78xx_fpga_load_devices()) { |
|
ts78xx_fpga.state = -1; |
|
return -EBUSY; |
|
} |
|
|
|
return 0; |
|
}; |
|
|
|
static int ts78xx_fpga_unload(void) |
|
{ |
|
unsigned int fpga_id; |
|
|
|
fpga_id = readl(TS78XX_FPGA_REGS_VIRT_BASE); |
|
|
|
/* |
|
* There does not seem to be a feasible way to block access to the GPIO |
|
* pins from userspace (/dev/mem). This if clause should hopefully warn |
|
* those foolish enough not to follow 'policy' :) |
|
* |
|
* UrJTAG SVN since r1381 can be used to reprogram the FPGA |
|
*/ |
|
if (ts78xx_fpga.id != fpga_id) { |
|
pr_err("FPGA magic/rev mismatch\n" |
|
"TS-78xx FPGA: was 0x%.6x/%.2x but now 0x%.6x/%.2x\n", |
|
(ts78xx_fpga.id >> 8) & 0xffffff, ts78xx_fpga.id & 0xff, |
|
(fpga_id >> 8) & 0xffffff, fpga_id & 0xff); |
|
ts78xx_fpga.state = -1; |
|
return -EBUSY; |
|
} |
|
|
|
if (ts78xx_fpga_unload_devices()) { |
|
ts78xx_fpga.state = -1; |
|
return -EBUSY; |
|
} |
|
|
|
return 0; |
|
}; |
|
|
|
static ssize_t ts78xx_fpga_show(struct kobject *kobj, |
|
struct kobj_attribute *attr, char *buf) |
|
{ |
|
if (ts78xx_fpga.state < 0) |
|
return sprintf(buf, "borked\n"); |
|
|
|
return sprintf(buf, "%s\n", (ts78xx_fpga.state) ? "online" : "offline"); |
|
} |
|
|
|
static ssize_t ts78xx_fpga_store(struct kobject *kobj, |
|
struct kobj_attribute *attr, const char *buf, size_t n) |
|
{ |
|
int value, ret; |
|
|
|
if (ts78xx_fpga.state < 0) { |
|
pr_err("FPGA borked, you must powercycle ASAP\n"); |
|
return -EBUSY; |
|
} |
|
|
|
if (strncmp(buf, "online", sizeof("online") - 1) == 0) |
|
value = 1; |
|
else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0) |
|
value = 0; |
|
else |
|
return -EINVAL; |
|
|
|
if (ts78xx_fpga.state == value) |
|
return n; |
|
|
|
ret = (ts78xx_fpga.state == 0) |
|
? ts78xx_fpga_load() |
|
: ts78xx_fpga_unload(); |
|
|
|
if (!(ret < 0)) |
|
ts78xx_fpga.state = value; |
|
|
|
return n; |
|
} |
|
|
|
static struct kobj_attribute ts78xx_fpga_attr = |
|
__ATTR(ts78xx_fpga, 0644, ts78xx_fpga_show, ts78xx_fpga_store); |
|
|
|
/***************************************************************************** |
|
* General Setup |
|
****************************************************************************/ |
|
static unsigned int ts78xx_mpp_modes[] __initdata = { |
|
MPP0_UNUSED, |
|
MPP1_GPIO, /* JTAG Clock */ |
|
MPP2_GPIO, /* JTAG Data In */ |
|
MPP3_GPIO, /* Lat ECP2 256 FPGA - PB2B */ |
|
MPP4_GPIO, /* JTAG Data Out */ |
|
MPP5_GPIO, /* JTAG TMS */ |
|
MPP6_GPIO, /* Lat ECP2 256 FPGA - PB31A_CLK4+ */ |
|
MPP7_GPIO, /* Lat ECP2 256 FPGA - PB22B */ |
|
MPP8_UNUSED, |
|
MPP9_UNUSED, |
|
MPP10_UNUSED, |
|
MPP11_UNUSED, |
|
MPP12_UNUSED, |
|
MPP13_UNUSED, |
|
MPP14_UNUSED, |
|
MPP15_UNUSED, |
|
MPP16_UART, |
|
MPP17_UART, |
|
MPP18_UART, |
|
MPP19_UART, |
|
/* |
|
* MPP[20] PCI Clock Out 1 |
|
* MPP[21] PCI Clock Out 0 |
|
* MPP[22] Unused |
|
* MPP[23] Unused |
|
* MPP[24] Unused |
|
* MPP[25] Unused |
|
*/ |
|
0, |
|
}; |
|
|
|
static void __init ts78xx_init(void) |
|
{ |
|
int ret; |
|
|
|
/* |
|
* Setup basic Orion functions. Need to be called early. |
|
*/ |
|
orion5x_init(); |
|
|
|
orion5x_mpp_conf(ts78xx_mpp_modes); |
|
|
|
/* |
|
* Configure peripherals. |
|
*/ |
|
orion5x_ehci0_init(); |
|
orion5x_ehci1_init(); |
|
orion5x_eth_init(&ts78xx_eth_data); |
|
orion5x_sata_init(&ts78xx_sata_data); |
|
orion5x_uart0_init(); |
|
orion5x_uart1_init(); |
|
orion5x_xor_init(); |
|
|
|
/* FPGA init */ |
|
ts78xx_fpga_devices_zero_init(); |
|
ret = ts78xx_fpga_load(); |
|
ret = sysfs_create_file(firmware_kobj, &ts78xx_fpga_attr.attr); |
|
if (ret) |
|
pr_err("sysfs_create_file failed: %d\n", ret); |
|
} |
|
|
|
MACHINE_START(TS78XX, "Technologic Systems TS-78xx SBC") |
|
/* Maintainer: Alexander Clouter <[email protected]> */ |
|
.atag_offset = 0x100, |
|
.nr_irqs = ORION5X_NR_IRQS, |
|
.init_machine = ts78xx_init, |
|
.map_io = ts78xx_map_io, |
|
.init_early = orion5x_init_early, |
|
.init_irq = orion5x_init_irq, |
|
.init_time = orion5x_timer_init, |
|
.restart = orion5x_restart, |
|
MACHINE_END
|
|
|