[ARM] orion5x: TS-78xx support for 'hotplug' of FPGA devices
authorAlexander Clouter <alex@digriz.org.uk>
Fri, 6 Feb 2009 22:16:55 +0000 (22:16 +0000)
committerAlexander Clouter <alex@digriz.org.uk>
Fri, 6 Feb 2009 22:16:55 +0000 (22:16 +0000)
the FPGA on the TS-7800 provides access to a number of devices
and so we have to be careful when reprogramming it.  As we
are effectively turning a bus off/on we have to inform the
kernel that it should stop using anything provided by the
FPGA (currently only the RTC however the NAND, LCD, etc is
to come) before it's reprogrammed.

Once reprogramed, we can tell the kernel to (re)enable things
by checking the FPGA ID against a lookup table for what a
particular FPGA bitstream can provide.

Signed-off-by: Alexander Clouter <alex@digriz.org.uk>
arch/arm/mach-orion5x/Kconfig
arch/arm/mach-orion5x/ts78xx-fpga.h [new file with mode: 0644]
arch/arm/mach-orion5x/ts78xx-setup.c

index f59a8d0..2c7035d 100644 (file)
@@ -71,6 +71,7 @@ config MACH_WRT350N_V2
 
 config MACH_TS78XX
        bool "Technologic Systems TS-78xx"
+       select PM
        help
          Say 'Y' here if you want your kernel to support the
          Technologic Systems TS-78xx platform.
diff --git a/arch/arm/mach-orion5x/ts78xx-fpga.h b/arch/arm/mach-orion5x/ts78xx-fpga.h
new file mode 100644 (file)
index 0000000..0b8e30f
--- /dev/null
@@ -0,0 +1,27 @@
+#define FPGAID(_magic, _rev) ((_magic << 8) + _rev)
+
+/*
+ * get yer id's from http://ts78xx.digriz.org.uk/
+ * do *not* make up your own or 'borrow' any!
+ */
+enum fpga_ids {
+       /* Technologic Systems */
+       TS7800_REV_B = FPGAID(0x00b480, 0x03),
+};
+
+struct fpga_device {
+       unsigned                present:1;
+       unsigned                init:1;
+};
+
+struct fpga_devices {
+       /* Technologic Systems */
+       struct fpga_device      ts_rtc;
+};
+
+struct ts78xx_fpga_data {
+       unsigned int            id;
+       int                     state;
+
+       struct fpga_devices     supports;
+};
index b194956..baa25d0 100644 (file)
@@ -10,6 +10,7 @@
 
 #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>
@@ -20,6 +21,7 @@
 #include <mach/orion5x.h>
 #include "common.h"
 #include "mpp.h"
+#include "ts78xx-fpga.h"
 
 /*****************************************************************************
  * TS-78xx Info
 #define TS78XX_FPGA_REGS_VIRT_BASE     0xff900000
 #define TS78XX_FPGA_REGS_SIZE          SZ_1M
 
-#define TS78XX_FPGA_REGS_SYSCON_ID     (TS78XX_FPGA_REGS_VIRT_BASE | 0x000)
-#define TS78XX_FPGA_REGS_SYSCON_LCDI   (TS78XX_FPGA_REGS_VIRT_BASE | 0x004)
-#define TS78XX_FPGA_REGS_SYSCON_LCDO   (TS78XX_FPGA_REGS_VIRT_BASE | 0x008)
-
-#define TS78XX_FPGA_REGS_RTC_CTRL      (TS78XX_FPGA_REGS_VIRT_BASE | 0x808)
-#define TS78XX_FPGA_REGS_RTC_DATA      (TS78XX_FPGA_REGS_VIRT_BASE | 0x80c)
+static struct ts78xx_fpga_data ts78xx_fpga = {
+       .id             = 0,
+       .state          = 1,
+/*     .supports       = ... - populated by ts78xx_fpga_supports() */
+};
 
 /*****************************************************************************
  * I/O Address Mapping
@@ -64,32 +65,42 @@ 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
  ****************************************************************************/
 #ifdef CONFIG_RTC_DRV_M48T86
-static unsigned char ts78xx_rtc_readbyte(unsigned long addr)
+#define TS_RTC_CTRL    (TS78XX_FPGA_REGS_VIRT_BASE | 0x808)
+#define TS_RTC_DATA    (TS78XX_FPGA_REGS_VIRT_BASE | 0x80c)
+
+static unsigned char ts78xx_ts_rtc_readbyte(unsigned long addr)
 {
-       writeb(addr, TS78XX_FPGA_REGS_RTC_CTRL);
-       return readb(TS78XX_FPGA_REGS_RTC_DATA);
+       writeb(addr, TS_RTC_CTRL);
+       return readb(TS_RTC_DATA);
 }
 
-static void ts78xx_rtc_writebyte(unsigned char value, unsigned long addr)
+static void ts78xx_ts_rtc_writebyte(unsigned char value, unsigned long addr)
 {
-       writeb(addr, TS78XX_FPGA_REGS_RTC_CTRL);
-       writeb(value, TS78XX_FPGA_REGS_RTC_DATA);
+       writeb(addr, TS_RTC_CTRL);
+       writeb(value, TS_RTC_DATA);
 }
 
-static struct m48t86_ops ts78xx_rtc_ops = {
-       .readbyte       = ts78xx_rtc_readbyte,
-       .writebyte      = ts78xx_rtc_writebyte,
+static struct m48t86_ops ts78xx_ts_rtc_ops = {
+       .readbyte       = ts78xx_ts_rtc_readbyte,
+       .writebyte      = ts78xx_ts_rtc_writebyte,
 };
 
-static struct platform_device ts78xx_rtc_device = {
+static struct platform_device ts78xx_ts_rtc_device = {
        .name           = "rtc-m48t86",
        .id             = -1,
        .dev            = {
-               .platform_data  = &ts78xx_rtc_ops,
+               .platform_data  = &ts78xx_ts_rtc_ops,
        },
        .num_resources  = 0,
 };
@@ -104,59 +115,185 @@ static struct platform_device ts78xx_rtc_device = {
  * TODO: track down a guinea pig without an RTC to see if we can work out a
  *             better RTC detection routine
  */
-static int __init ts78xx_rtc_init(void)
+static int ts78xx_ts_rtc_load(void)
 {
        unsigned char tmp_rtc0, tmp_rtc1;
 
-       tmp_rtc0 = ts78xx_rtc_readbyte(126);
-       tmp_rtc1 = ts78xx_rtc_readbyte(127);
-
-       ts78xx_rtc_writebyte(0x00, 126);
-       ts78xx_rtc_writebyte(0x55, 127);
-       if (ts78xx_rtc_readbyte(127) == 0x55) {
-               ts78xx_rtc_writebyte(0xaa, 127);
-               if (ts78xx_rtc_readbyte(127) == 0xaa
-                               && ts78xx_rtc_readbyte(126) == 0x00) {
-                       ts78xx_rtc_writebyte(tmp_rtc0, 126);
-                       ts78xx_rtc_writebyte(tmp_rtc1, 127);
-                       platform_device_register(&ts78xx_rtc_device);
-                       return 1;
+       tmp_rtc0 = ts78xx_ts_rtc_readbyte(126);
+       tmp_rtc1 = ts78xx_ts_rtc_readbyte(127);
+
+       ts78xx_ts_rtc_writebyte(0x00, 126);
+       ts78xx_ts_rtc_writebyte(0x55, 127);
+       if (ts78xx_ts_rtc_readbyte(127) == 0x55) {
+               ts78xx_ts_rtc_writebyte(0xaa, 127);
+               if (ts78xx_ts_rtc_readbyte(127) == 0xaa
+                               && ts78xx_ts_rtc_readbyte(126) == 0x00) {
+                       ts78xx_ts_rtc_writebyte(tmp_rtc0, 126);
+                       ts78xx_ts_rtc_writebyte(tmp_rtc1, 127);
+                       if (ts78xx_fpga.supports.ts_rtc.init == 0) {
+                               ts78xx_fpga.supports.ts_rtc.init = 1;
+                               platform_device_register(&ts78xx_ts_rtc_device);
+                       } else
+                               platform_device_add(&ts78xx_ts_rtc_device);
+                       return 0;
                }
        }
 
-       return 0;
+       ts78xx_fpga.supports.ts_rtc.present = 0;
+       return -ENODEV;
 };
+
+static void ts78xx_ts_rtc_unload(void)
+{
+       platform_device_del(&ts78xx_ts_rtc_device);
+}
 #else
-static int __init ts78xx_rtc_init(void)
+static int ts78xx_ts_rtc_load(void)
 {
        return 0;
 }
+
+static void ts78xx_ts_rtc_unload(void)
+{
+}
 #endif
 
 /*****************************************************************************
- * SATA
+ * FPGA 'hotplug' support code
  ****************************************************************************/
-static struct mv_sata_platform_data ts78xx_sata_data = {
-       .n_ports        = 2,
+static void ts78xx_fpga_devices_zero_init(void)
+{
+       ts78xx_fpga.supports.ts_rtc.init = 0;
+}
+
+static void ts78xx_fpga_supports(void)
+{
+       /* TODO: put this 'table' into ts78xx-fpga.h */
+       switch (ts78xx_fpga.id) {
+       case TS7800_REV_B:
+               ts78xx_fpga.supports.ts_rtc.present = 1;
+               break;
+       default:
+               ts78xx_fpga.supports.ts_rtc.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)
+                       printk(KERN_INFO "TS-78xx RTC not detected or enabled\n");
+               ret |= tmp;
+       }
+
+       return ret;
+}
+
+static int ts78xx_fpga_unload_devices(void)
+{
+       int ret = 0;
+
+       if (ts78xx_fpga.supports.ts_rtc.present == 1)
+               ts78xx_ts_rtc_unload();
+
+       return ret;
+}
+
+static int ts78xx_fpga_load(void)
+{
+       ts78xx_fpga.id = readl(TS78XX_FPGA_REGS_VIRT_BASE);
+
+       printk(KERN_INFO "TS-78xx 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;
 };
 
-/*****************************************************************************
- * print some information regarding the board
- ****************************************************************************/
-static void __init ts78xx_print_board_id(void)
-{
-       unsigned int board_info;
-
-       board_info = readl(TS78XX_FPGA_REGS_SYSCON_ID);
-       printk(KERN_INFO "TS-78xx Info: FPGA rev=%.2x, Board Magic=%.6x, ",
-                               board_info & 0xff,
-                               (board_info >> 8) & 0xffffff);
-       board_info = readl(TS78XX_FPGA_REGS_SYSCON_LCDI);
-       printk("JP1=%d, JP2=%d\n",
-                               (board_info >> 30) & 0x1,
-                               (board_info >> 31) & 0x1);
+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) {
+               printk(KERN_ERR "TS-78xx 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) {
+               printk(KERN_ERR "TS-78xx 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 {
+               printk(KERN_ERR "ts78xx_fpga_store: Invalid value\n");
+               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
  ****************************************************************************/
@@ -194,13 +331,13 @@ static struct orion5x_mpp_mode ts78xx_mpp_modes[] __initdata = {
 
 static void __init ts78xx_init(void)
 {
+       int ret;
+
        /*
         * Setup basic Orion functions. Need to be called early.
         */
        orion5x_init();
 
-       ts78xx_print_board_id();
-
        orion5x_mpp_conf(ts78xx_mpp_modes);
 
        /*
@@ -214,8 +351,12 @@ static void __init ts78xx_init(void)
        orion5x_uart1_init();
        orion5x_xor_init();
 
-       if (!ts78xx_rtc_init())
-               printk(KERN_INFO "TS-78xx RTC not detected or enabled\n");
+       /* FPGA init */
+       ts78xx_fpga_devices_zero_init();
+       ret = ts78xx_fpga_load();
+       ret = sysfs_create_file(power_kobj, &ts78xx_fpga_attr.attr);
+       if (ret)
+               printk(KERN_ERR "sysfs_create_file failed: %d\n", ret);
 }
 
 MACHINE_START(TS78XX, "Technologic Systems TS-78xx SBC")