|
@@ -67,3 +67,174 @@
|
|
|
#include "devices.h"
|
|
|
|
|
|
static unsigned int icr;
|
|
|
+
|
|
|
+static void viper_icr_set_bit(unsigned int bit)
|
|
|
+{
|
|
|
+ icr |= bit;
|
|
|
+ VIPER_ICR = icr;
|
|
|
+}
|
|
|
+
|
|
|
+static void viper_icr_clear_bit(unsigned int bit)
|
|
|
+{
|
|
|
+ icr &= ~bit;
|
|
|
+ VIPER_ICR = icr;
|
|
|
+}
|
|
|
+
|
|
|
+/* This function is used from the pcmcia module to reset the CF */
|
|
|
+static void viper_cf_reset(int state)
|
|
|
+{
|
|
|
+ if (state)
|
|
|
+ viper_icr_set_bit(VIPER_ICR_CF_RST);
|
|
|
+ else
|
|
|
+ viper_icr_clear_bit(VIPER_ICR_CF_RST);
|
|
|
+}
|
|
|
+
|
|
|
+static struct arcom_pcmcia_pdata viper_pcmcia_info = {
|
|
|
+ .cd_gpio = VIPER_CF_CD_GPIO,
|
|
|
+ .rdy_gpio = VIPER_CF_RDY_GPIO,
|
|
|
+ .pwr_gpio = VIPER_CF_POWER_GPIO,
|
|
|
+ .reset = viper_cf_reset,
|
|
|
+};
|
|
|
+
|
|
|
+static struct platform_device viper_pcmcia_device = {
|
|
|
+ .name = "viper-pcmcia",
|
|
|
+ .id = -1,
|
|
|
+ .dev = {
|
|
|
+ .platform_data = &viper_pcmcia_info,
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * The CPLD version register was not present on VIPER boards prior to
|
|
|
+ * v2i1. On v1 boards where the version register is not present we
|
|
|
+ * will just read back the previous value from the databus.
|
|
|
+ *
|
|
|
+ * Therefore we do two reads. The first time we write 0 to the
|
|
|
+ * (read-only) register before reading and the second time we write
|
|
|
+ * 0xff first. If the two reads do not match or they read back as 0xff
|
|
|
+ * or 0x00 then we have version 1 hardware.
|
|
|
+ */
|
|
|
+static u8 viper_hw_version(void)
|
|
|
+{
|
|
|
+ u8 v1, v2;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ local_irq_save(flags);
|
|
|
+
|
|
|
+ VIPER_VERSION = 0;
|
|
|
+ v1 = VIPER_VERSION;
|
|
|
+ VIPER_VERSION = 0xff;
|
|
|
+ v2 = VIPER_VERSION;
|
|
|
+
|
|
|
+ v1 = (v1 != v2 || v1 == 0xff) ? 0 : v1;
|
|
|
+
|
|
|
+ local_irq_restore(flags);
|
|
|
+ return v1;
|
|
|
+}
|
|
|
+
|
|
|
+/* CPU system core operations. */
|
|
|
+static int viper_cpu_suspend(void)
|
|
|
+{
|
|
|
+ viper_icr_set_bit(VIPER_ICR_R_DIS);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void viper_cpu_resume(void)
|
|
|
+{
|
|
|
+ viper_icr_clear_bit(VIPER_ICR_R_DIS);
|
|
|
+}
|
|
|
+
|
|
|
+static struct syscore_ops viper_cpu_syscore_ops = {
|
|
|
+ .suspend = viper_cpu_suspend,
|
|
|
+ .resume = viper_cpu_resume,
|
|
|
+};
|
|
|
+
|
|
|
+static unsigned int current_voltage_divisor;
|
|
|
+
|
|
|
+/*
|
|
|
+ * If force is not true then step from existing to new divisor. If
|
|
|
+ * force is true then jump straight to the new divisor. Stepping is
|
|
|
+ * used because if the jump in voltage is too large, the VCC can dip
|
|
|
+ * too low and the regulator cuts out.
|
|
|
+ *
|
|
|
+ * force can be used to initialize the divisor to a know state by
|
|
|
+ * setting the value for the current clock speed, since we are already
|
|
|
+ * running at that speed we know the voltage should be pretty close so
|
|
|
+ * the jump won't be too large
|
|
|
+ */
|
|
|
+static void viper_set_core_cpu_voltage(unsigned long khz, int force)
|
|
|
+{
|
|
|
+ int i = 0;
|
|
|
+ unsigned int divisor = 0;
|
|
|
+ const char *v;
|
|
|
+
|
|
|
+ if (khz < 200000) {
|
|
|
+ v = "1.0"; divisor = 0xfff;
|
|
|
+ } else if (khz < 300000) {
|
|
|
+ v = "1.1"; divisor = 0xde5;
|
|
|
+ } else {
|
|
|
+ v = "1.3"; divisor = 0x325;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_debug("viper: setting CPU core voltage to %sV at %d.%03dMHz\n",
|
|
|
+ v, (int)khz / 1000, (int)khz % 1000);
|
|
|
+
|
|
|
+#define STEP 0x100
|
|
|
+ do {
|
|
|
+ int step;
|
|
|
+
|
|
|
+ if (force)
|
|
|
+ step = divisor;
|
|
|
+ else if (current_voltage_divisor < divisor - STEP)
|
|
|
+ step = current_voltage_divisor + STEP;
|
|
|
+ else if (current_voltage_divisor > divisor + STEP)
|
|
|
+ step = current_voltage_divisor - STEP;
|
|
|
+ else
|
|
|
+ step = divisor;
|
|
|
+ force = 0;
|
|
|
+
|
|
|
+ gpio_set_value(VIPER_PSU_CLK_GPIO, 0);
|
|
|
+ gpio_set_value(VIPER_PSU_nCS_LD_GPIO, 0);
|
|
|
+
|
|
|
+ for (i = 1 << 11 ; i > 0 ; i >>= 1) {
|
|
|
+ udelay(1);
|
|
|
+
|
|
|
+ gpio_set_value(VIPER_PSU_DATA_GPIO, step & i);
|
|
|
+ udelay(1);
|
|
|
+
|
|
|
+ gpio_set_value(VIPER_PSU_CLK_GPIO, 1);
|
|
|
+ udelay(1);
|
|
|
+
|
|
|
+ gpio_set_value(VIPER_PSU_CLK_GPIO, 0);
|
|
|
+ }
|
|
|
+ udelay(1);
|
|
|
+
|
|
|
+ gpio_set_value(VIPER_PSU_nCS_LD_GPIO, 1);
|
|
|
+ udelay(1);
|
|
|
+
|
|
|
+ gpio_set_value(VIPER_PSU_nCS_LD_GPIO, 0);
|
|
|
+
|
|
|
+ current_voltage_divisor = step;
|
|
|
+ } while (current_voltage_divisor != divisor);
|
|
|
+}
|
|
|
+
|
|
|
+/* Interrupt handling */
|
|
|
+static unsigned long viper_irq_enabled_mask;
|
|
|
+static const int viper_isa_irqs[] = { 3, 4, 5, 6, 7, 10, 11, 12, 9, 14, 15 };
|
|
|
+static const int viper_isa_irq_map[] = {
|
|
|
+ 0, /* ISA irq #0, invalid */
|
|
|
+ 0, /* ISA irq #1, invalid */
|
|
|
+ 0, /* ISA irq #2, invalid */
|
|
|
+ 1 << 0, /* ISA irq #3 */
|
|
|
+ 1 << 1, /* ISA irq #4 */
|
|
|
+ 1 << 2, /* ISA irq #5 */
|
|
|
+ 1 << 3, /* ISA irq #6 */
|
|
|
+ 1 << 4, /* ISA irq #7 */
|
|
|
+ 0, /* ISA irq #8, invalid */
|
|
|
+ 1 << 8, /* ISA irq #9 */
|
|
|
+ 1 << 5, /* ISA irq #10 */
|
|
|
+ 1 << 6, /* ISA irq #11 */
|
|
|
+ 1 << 7, /* ISA irq #12 */
|
|
|
+ 0, /* ISA irq #13, invalid */
|
|
|
+ 1 << 9, /* ISA irq #14 */
|
|
|
+ 1 << 10, /* ISA irq #15 */
|