aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/files/0007-Quark-Platform-Code-quark.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-kernel/linux/files/0007-Quark-Platform-Code-quark.patch')
-rw-r--r--recipes-kernel/linux/files/0007-Quark-Platform-Code-quark.patch6501
1 files changed, 6501 insertions, 0 deletions
diff --git a/recipes-kernel/linux/files/0007-Quark-Platform-Code-quark.patch b/recipes-kernel/linux/files/0007-Quark-Platform-Code-quark.patch
new file mode 100644
index 0000000..21f2d7f
--- /dev/null
+++ b/recipes-kernel/linux/files/0007-Quark-Platform-Code-quark.patch
@@ -0,0 +1,6501 @@
+From xxxx Mon Sep 17 00:00:00 2001
+From: Quark Team <noreply@intel.com>
+Date: Wed, 26 Feb 2014 14:40:08 +0000
+Subject: [PATCH 07/21] Quark Platform Code
+
+---
+ drivers/platform/x86/Kconfig | 4 +
+ drivers/platform/x86/Makefile | 1 +
+ drivers/platform/x86/quark/Kconfig | 41 +
+ drivers/platform/x86/quark/Makefile | 14 +
+ drivers/platform/x86/quark/intel_qrk_audio_ctrl.c | 514 +++++++++
+ drivers/platform/x86/quark/intel_qrk_audio_ctrl.h | 45 +
+ drivers/platform/x86/quark/intel_qrk_board_data.c | 260 +++++
+ drivers/platform/x86/quark/intel_qrk_esram.c | 1144 ++++++++++++++++++++
+ drivers/platform/x86/quark/intel_qrk_esram.h | 107 ++
+ drivers/platform/x86/quark/intel_qrk_esram_test.c | 602 ++++++++++
+ drivers/platform/x86/quark/intel_qrk_esram_test.h | 43 +
+ drivers/platform/x86/quark/intel_qrk_imr.c | 697 ++++++++++++
+ drivers/platform/x86/quark/intel_qrk_imr.h | 157 +++
+ drivers/platform/x86/quark/intel_qrk_imr_kernel.c | 139 +++
+ drivers/platform/x86/quark/intel_qrk_imr_test.c | 357 ++++++
+ .../x86/quark/intel_qrk_plat_clanton_hill.c | 226 ++++
+ .../x86/quark/intel_qrk_plat_clanton_peak.c | 227 ++++
+ .../platform/x86/quark/intel_qrk_plat_cross_hill.c | 392 +++++++
+ .../platform/x86/quark/intel_qrk_plat_galileo.c | 398 +++++++
+ .../platform/x86/quark/intel_qrk_plat_kips_bay.c | 176 +++
+ drivers/platform/x86/quark/intel_qrk_sb.c | 253 +++++
+ drivers/platform/x86/quark/intel_qrk_thermal.c | 360 ++++++
+ include/linux/intel_qrk_sb.h | 92 ++
+ include/linux/platform_data/quark.h | 44 +
+ 24 files changed, 6293 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/platform/x86/quark/Kconfig
+ create mode 100644 drivers/platform/x86/quark/Makefile
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_audio_ctrl.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_audio_ctrl.h
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_board_data.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_esram.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_esram.h
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_esram_test.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_esram_test.h
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_imr.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_imr.h
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_imr_kernel.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_imr_test.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_plat_clanton_hill.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_plat_clanton_peak.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_plat_cross_hill.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_plat_galileo.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_plat_kips_bay.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_sb.c
+ create mode 100644 drivers/platform/x86/quark/intel_qrk_thermal.c
+ create mode 100644 include/linux/intel_qrk_sb.h
+ create mode 100644 include/linux/platform_data/quark.h
+
+diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
+index c86bae8..6def5f6 100644
+--- a/drivers/platform/x86/Kconfig
++++ b/drivers/platform/x86/Kconfig
+@@ -672,6 +672,10 @@ config INTEL_MFLD_THERMAL
+ Say Y here to enable thermal driver support for the Intel Medfield
+ platform.
+
++if INTEL_QUARK_X1000_SOC
++source "drivers/platform/x86/quark/Kconfig"
++endif
++
+ config INTEL_IPS
+ tristate "Intel Intelligent Power Sharing"
+ depends on ACPI
+diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
+index bf7e4f9..fce76ef 100644
+--- a/drivers/platform/x86/Makefile
++++ b/drivers/platform/x86/Makefile
+@@ -50,3 +50,4 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
+ obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
+ obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
+ obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += quark/
+diff --git a/drivers/platform/x86/quark/Kconfig b/drivers/platform/x86/quark/Kconfig
+new file mode 100644
+index 0000000..0e2c722
+--- /dev/null
++++ b/drivers/platform/x86/quark/Kconfig
+@@ -0,0 +1,41 @@
++config INTEL_QRK_ESRAM
++ bool "eSRAM - embedded SRAM driver for Intel Quark platform"
++ depends on INTEL_QUARK_X1000_SOC && PM
++ select KALLSYMS
++ select CRC16
++ help
++ Say Y here to enable eSRAM overlay and software-initiated ECC
++ updates. eSRAM overlaying allows for code/data structures to be
++ mapped into eSRAM thus providing far faster access to code/data
++ than ordinary DRAM. Slower than cache RAM faster than DRAM.
++
++config INTEL_QRK_ECC_REFRESH_PERIOD
++ int "Choose eSRAM ECC coverage period"
++ depends on INTEL_QRK_ESRAM
++ default 24
++ help
++ Select the period over which *RAM ECC codes should be refreshed.
++ IA Core will periodically enable disabled eSRAM pages to ensure all of
++ disabled eSRAM pages are 'address walked' in this period. A logical
++ component within the silicon on Quark will ensure DRAM (and
++ overlayed eSRAM) pages by extension are similarly updated over the
++ same period. This variable controlls how long a time this address
++ walking algorithm should take. For a noisy environment like a
++ sub-station or a satellite update frequently. For less noisy
++ environments this value should be lower. Default 24 hours is right for
++ most people. Set to zero to disable - this is NOT recommended. Max 48
++ hours.
++
++config INTEL_QRK_THERMAL
++ bool "Thermal driver for Intel Quark platform"
++ depends on INTEL_QUARK_X1000_SOC
++ help
++ Say Y here to enable Quark's Thermal driver plus the MSI's
++ that can be hooked from the thermal sub-system
++
++config INTEL_QRK_AUDIO_CTRL
++ tristate "Audio sub-system control driver for Intel Quark platform"
++ depends on INTEL_QUARK_X1000_SOC
++ help
++ Say Y here to enable Quark's audio control driver
++
+diff --git a/drivers/platform/x86/quark/Makefile b/drivers/platform/x86/quark/Makefile
+new file mode 100644
+index 0000000..0a03469
+--- /dev/null
++++ b/drivers/platform/x86/quark/Makefile
+@@ -0,0 +1,14 @@
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_qrk_board_data.o
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_qrk_plat_clanton_hill.o
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_qrk_plat_clanton_peak.o
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_qrk_plat_cross_hill.o
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_qrk_plat_kips_bay.o
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_qrk_plat_galileo.o
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_qrk_sb.o
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_qrk_imr.o
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_qrk_imr_kernel.o
++obj-$(CONFIG_INTEL_QRK_ESRAM) += intel_qrk_esram.o
++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_qrk_imr_test.o
++obj-$(CONFIG_INTEL_QRK_ESRAM) += intel_qrk_esram_test.o
++obj-$(CONFIG_INTEL_QRK_THERMAL) += intel_qrk_thermal.o
++obj-$(CONFIG_INTEL_QRK_AUDIO_CTRL) += intel_qrk_audio_ctrl.o
+diff --git a/drivers/platform/x86/quark/intel_qrk_audio_ctrl.c b/drivers/platform/x86/quark/intel_qrk_audio_ctrl.c
+new file mode 100644
+index 0000000..ffc3791
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_audio_ctrl.c
+@@ -0,0 +1,514 @@
++/*
++ * Intel Quark platform audio control driver
++ *
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ *
++ * The Intel Clanton Hill platform hardware design includes an audio subsystem
++ * with a number of interconnected audio interfaces. This driver enables
++ * applications to choose which audio connections to enable for various
++ * application use cases. The interconnections are selectable using GPIO output
++ * pins on the CPU. This driver is also responsible for configuring a Maxim
++ * 9867 audio codec, a component of this audio subsystem, connected to the CPU
++ * via I2C.
++ */
++
++#include <linux/module.h>
++#include <linux/printk.h>
++#include <linux/platform_device.h>
++#include <linux/types.h>
++#include <linux/err.h>
++#include <linux/i2c.h>
++#include <linux/gpio.h>
++#include <linux/cdev.h>
++#include <linux/fs.h>
++#include <uapi/linux/ioctl.h>
++#include <linux/mutex.h>
++#include <linux/sysfs.h>
++
++#include "intel_qrk_audio_ctrl.h"
++
++#define DRIVER_NAME "intel_qrk_audio_ctrl"
++
++/*
++ * GPIO numbers to use for switching audio paths
++ */
++#define GPIO_AUDIO_S0 11
++#define GPIO_AUDIO_S1 12
++#define GPIO_AUDIO_S2 13
++
++#define GPIO_AUDIO_DEFAULT (INTEL_QRK_AUDIO_MODE_SPKR_MIC)
++
++/**
++ * struct intel_qrk_audio_ctrl_data
++ *
++ * Structure to represent module state/data/etc
++ */
++struct intel_qrk_audio_ctrl_priv {
++
++ /* i2c device descriptor for read/write access to MAX9867 registers */
++ struct i2c_client *max9867_i2c;
++
++ /* Char dev to provide user-space ioctl interface for audio control */
++ struct cdev cdev;
++ dev_t cdev_no;
++ struct class *cl;
++
++ /* Mutex to protect against concurrent access to the ioctl() handler */
++ struct mutex lock;
++
++ /* Current GPIO switch value */
++ unsigned char gpio_val;
++};
++
++static int
++intel_qrk_audio_ctrl_open(struct inode *inode, struct file *filp)
++{
++ struct intel_qrk_audio_ctrl_priv *priv;
++
++ priv = container_of(inode->i_cdev,
++ struct intel_qrk_audio_ctrl_priv,
++ cdev);
++ filp->private_data = priv;
++
++ return 0;
++}
++
++static int
++intel_qrk_audio_ctrl_release(struct inode *inode, struct file *filp)
++{
++ return 0;
++}
++
++/*
++ * Logic truth table for AUDIO_S[0-3] outputs, illustrating which paths are
++ * connected between audio interfaces A, B, C. Each audio interface has one
++ * effective input (I) port and one effective output (O) port
++ *
++ * A = USB Codec (to Quark CPU)
++ * B = Spkr/Mic (to car audio system)
++ * C = I2S Codec (to Telit HE910)
++ *
++ * PATH examples:
++ * AO-CO: A-Output connected to C-Output
++ * BI-AI: B-Input connected to A-Input
++ *
++ * NOTE: Assume a CI-AI connection is available in ALL cases (sometimes unused)
++ *
++ * S2 S1 S0 PATHS USE CASE
++ * -- -- -- ----------------- -------------------------------------------------
++ * 0 0 0 AO-CO BT Headset call
++ * 0 0 1 AO-BO Analog Driver Alerts (CI unused)
++ * 0 1 0 AO-CO,BI-AI XX Unused/invalid (BI *and* CI connected to AI)
++ * 0 1 1 AO-BO,BI-AI Archival Voice Record/Playback (or Driver Alerts)
++ * 1 0 0 AO-CO,BI-CO XX Unused/invalid (A0 *and* BI connected to CO)
++ * 1 0 1 AO-BO,BI-CO Analog hands-free call
++ * 1 1 0 AO-CO,BI-AI,BI-CO XX Unused/invalid (BI connected to AI *and* CO)
++ * 1 1 1 AO-BO,BI-AI,BI-CO XX Unused/invalid (BI connected to AI *and* CO)
++ *
++ *
++ * Mapping to IOCTLs (using more intuitive naming on the API):
++ *
++ * PATHS IOCTL
++ * --------------- -------------------------------------------------------------
++ * AO-CO INTEL_QRK_AUDIO_MODE_GSM_ONLY
++ * AO-BO INTEL_QRK_AUDIO_MODE_SPKR_ONLY
++ * AO-BO,BI-AI INTEL_QRK_AUDIO_MODE_SPKR_MIC
++ * AO-BO,BI-CO INTEL_QRK_AUDIO_MODE_GSM_SPKR_MIC
++ */
++
++static int
++intel_qrk_audio_ctrl_gpio_update(struct intel_qrk_audio_ctrl_priv *priv)
++{
++ int ret = 0;
++ struct gpio audio_sw_gpios[] = {
++ {
++ GPIO_AUDIO_S2,
++ GPIOF_OUT_INIT_LOW,
++ "audio_s2"
++ },
++ {
++ GPIO_AUDIO_S1,
++ GPIOF_OUT_INIT_LOW,
++ "audio_s1"
++ },
++ {
++ GPIO_AUDIO_S0,
++ GPIOF_OUT_INIT_LOW,
++ "audio_s0"
++ }
++ };
++
++ /*
++ * Update the Audio Switch GPIO outputs according to the user selection
++ */
++ ret = gpio_request_array(audio_sw_gpios,
++ ARRAY_SIZE(audio_sw_gpios));
++ if (ret) {
++ pr_err("%s: Failed to allocate audio control GPIO pins\n",
++ __func__);
++ return ret;
++ }
++
++ gpio_set_value(GPIO_AUDIO_S2, (priv->gpio_val >> 2) & 0x1);
++ gpio_set_value(GPIO_AUDIO_S1, (priv->gpio_val >> 1) & 0x1);
++ gpio_set_value(GPIO_AUDIO_S0, (priv->gpio_val >> 0) & 0x1);
++
++ gpio_free_array(audio_sw_gpios,
++ ARRAY_SIZE(audio_sw_gpios));
++
++ return 0;
++}
++
++static long
++intel_qrk_audio_ctrl_ioctl(struct file *filp,
++ unsigned int cmd,
++ unsigned long arg)
++{
++ struct intel_qrk_audio_ctrl_priv *priv = filp->private_data;
++ int ret = 0;
++
++ ret = mutex_lock_interruptible(&priv->lock);
++ if (ret)
++ return ret;
++
++ switch (cmd) {
++ case INTEL_QRK_AUDIO_MODE_IOC_GSM_ONLY:
++ case INTEL_QRK_AUDIO_MODE_IOC_SPKR_ONLY:
++ case INTEL_QRK_AUDIO_MODE_IOC_SPKR_MIC:
++ case INTEL_QRK_AUDIO_MODE_IOC_GSM_SPKR_MIC:
++ break;
++ default:
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ priv->gpio_val = _IOC_NR(cmd) & 0x7;
++ ret = intel_qrk_audio_ctrl_gpio_update(priv);
++exit:
++ mutex_unlock(&priv->lock);
++ return ret;
++}
++
++static const struct file_operations intel_qrk_audio_ctrl_fops = {
++ .owner = THIS_MODULE,
++ .open = intel_qrk_audio_ctrl_open,
++ .release = intel_qrk_audio_ctrl_release,
++ .unlocked_ioctl = intel_qrk_audio_ctrl_ioctl
++};
++
++static int
++intel_qrk_audio_ctrl_chrdev_init(struct intel_qrk_audio_ctrl_priv *priv)
++{
++ /* Register a character dev interface (with ioctls)
++ * to allow control of the audio subsystem switch
++ */
++ int ret;
++ struct device *dev;
++
++ ret = alloc_chrdev_region(&priv->cdev_no, 0, 1,
++ "intel_qrk_audio_ctrl");
++ if (ret) {
++ pr_err("Failed to alloc chrdev: %d", ret);
++ return ret;
++ }
++
++ cdev_init(&priv->cdev, &intel_qrk_audio_ctrl_fops);
++
++ ret = cdev_add(&priv->cdev, priv->cdev_no, 1);
++ if (ret) {
++ pr_err("Failed to add cdev: %d", ret);
++ unregister_chrdev_region(priv->cdev_no, 1);
++ return ret;
++ }
++
++ priv->cl = class_create(THIS_MODULE, "char");
++ if (IS_ERR(priv->cl)) {
++ pr_err("Failed to create device class: %ld",
++ PTR_ERR(priv->cl));
++ cdev_del(&priv->cdev);
++ unregister_chrdev_region(priv->cdev_no, 1);
++ return PTR_ERR(priv->cl);
++ }
++
++ dev = device_create(priv->cl, NULL, priv->cdev_no, NULL,
++ "intel_qrk_audio_ctrl");
++ if (IS_ERR(dev)) {
++ pr_err("Failed to create device: %ld",
++ PTR_ERR(priv->cl));
++ class_destroy(priv->cl);
++ cdev_del(&priv->cdev);
++ unregister_chrdev_region(priv->cdev_no, 1);
++ return PTR_ERR(dev);
++ }
++
++ return 0;
++}
++
++static int
++intel_qrk_audio_ctrl_chrdev_remove(struct intel_qrk_audio_ctrl_priv *priv)
++{
++ device_destroy(priv->cl, priv->cdev_no);
++ class_destroy(priv->cl);
++ cdev_del(&priv->cdev);
++ unregister_chrdev_region(priv->cdev_no, 1);
++
++ return 0;
++}
++
++
++ssize_t intel_qrk_audio_ctrl_sysfs_show_mode(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct intel_qrk_audio_ctrl_priv *priv = dev_get_drvdata(dev);
++ int ret;
++ char *mode;
++
++ ret = mutex_lock_interruptible(&priv->lock);
++ if (ret)
++ return ret;
++
++ switch (priv->gpio_val) {
++ case INTEL_QRK_AUDIO_MODE_GSM_ONLY:
++ mode = "gsm";
++ break;
++ case INTEL_QRK_AUDIO_MODE_SPKR_ONLY:
++ mode = "spkr";
++ break;
++ case INTEL_QRK_AUDIO_MODE_SPKR_MIC:
++ mode = "spkr_mic";
++ break;
++ case INTEL_QRK_AUDIO_MODE_GSM_SPKR_MIC:
++ mode = "gsm_spkr_mic";
++ break;
++ default:
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = scnprintf(buf, PAGE_SIZE, "%s\n", mode);
++
++exit:
++ mutex_unlock(&priv->lock);
++ return ret;
++}
++
++ssize_t intel_qrk_audio_ctrl_sysfs_store_mode(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct intel_qrk_audio_ctrl_priv *priv = dev_get_drvdata(dev);
++ char mode[16];
++ unsigned char gpio_val;
++ int ret = count;
++
++ sscanf(buf, "%15s", mode);
++
++ if (!strcmp(mode, "gsm"))
++ gpio_val = INTEL_QRK_AUDIO_MODE_GSM_ONLY;
++ else if (!strcmp(mode, "spkr"))
++ gpio_val = INTEL_QRK_AUDIO_MODE_SPKR_ONLY;
++ else if (!strcmp(mode, "spkr_mic"))
++ gpio_val = INTEL_QRK_AUDIO_MODE_SPKR_MIC;
++ else if (!strcmp(mode, "gsm_spkr_mic"))
++ gpio_val = INTEL_QRK_AUDIO_MODE_GSM_SPKR_MIC;
++ else
++ return -EINVAL;
++
++ ret = mutex_lock_interruptible(&priv->lock);
++ if (ret)
++ return ret;
++
++ priv->gpio_val = gpio_val;
++ ret = intel_qrk_audio_ctrl_gpio_update(priv);
++ if (ret)
++ goto exit;
++
++ ret = count;
++
++exit:
++ mutex_unlock(&priv->lock);
++
++ return ret;
++}
++
++/* Sysfs attribute descriptor (for alternative user-space interface) */
++static DEVICE_ATTR(audio_switch_mode, S_IWUSR | S_IRUGO,
++ intel_qrk_audio_ctrl_sysfs_show_mode,
++ intel_qrk_audio_ctrl_sysfs_store_mode);
++
++/******************************************************************************
++ * Module hooks
++ ******************************************************************************/
++
++static int
++intel_qrk_max9867_init(struct i2c_client *client)
++{
++ int ret;
++
++ /* MAX9867 register configuration, from Telit HE910 DVI app-note */
++
++ u8 reg_cfg_seq1[] = {
++ 0x04, /* Starting register address, followed by data */
++ 0x00, /* 0x04 Interrupt Enable */
++ 0x10, /* 0x05 System Clock */
++ 0x90, /* 0x06 Audio Clock High */
++ 0x00, /* 0x07 Audio Clock Low */
++ 0x10, /* 0x08 Interface 1a */
++ 0x0A, /* 0x09 Interface 1d */
++ 0x33, /* 0x0A Codec Filters */
++ 0x00, /* 0x0B DAC Gain/Sidetone */
++ 0x00, /* 0x0C DAC Level */
++ 0x33, /* 0x0D ADC Level */
++ 0x4C, /* 0x0E Left Line Input Level */
++ 0x4C, /* 0x0F Right Line Input Level */
++ 0x00, /* 0x10 Left Volume Control */
++ 0x00, /* 0x11 Right Volume Control */
++ 0x14, /* 0x12 Left Mic Gain */
++ 0x14, /* 0x13 Right Mic Gain */
++ /* Configuration */
++ 0xA0, /* 0x14 Input */
++ 0x00, /* 0x15 Microphone */
++ 0x65 /* 0x16 Mode */
++ };
++
++ u8 reg_cfg_seq2[] = {
++ 0x17, /* Starting register address, followed by data */
++ 0xEF /* 0x17 System Shutdown */
++ };
++
++ ret = i2c_master_send(client,
++ reg_cfg_seq1, sizeof(reg_cfg_seq1));
++ if (ret != sizeof(reg_cfg_seq1)) {
++ pr_err("Failed to write MAX9867 config registers (set 1/2)");
++ return -EIO;
++ }
++
++ ret = i2c_master_send(client,
++ reg_cfg_seq2, sizeof(reg_cfg_seq2));
++ if (ret != sizeof(reg_cfg_seq2)) {
++ pr_err("Failed to write MAX9867 config registers (set 2/2)");
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static int
++intel_qrk_max9867_get_chip_rev(struct i2c_client *client)
++{
++ struct i2c_msg msg[2];
++ u8 data[2];
++ int ret;
++
++ data[0] = 0xFF; /* Chip-revision register address = 0xFF */
++ msg[0].addr = client->addr;
++ msg[0].flags = 0;
++ msg[0].buf = &data[0];
++ msg[0].len = 1;
++
++ msg[1].addr = client->addr;
++ msg[1].flags = I2C_M_RD;
++ msg[1].buf = &data[1];
++ msg[1].len = 1;
++
++ ret = i2c_transfer(client->adapter, &msg[0], 2);
++ return (ret == 2) ? data[1] : -EIO;
++}
++
++static int intel_qrk_max9867_i2c_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct intel_qrk_audio_ctrl_priv *priv;
++ int ret;
++
++ priv = devm_kzalloc(&client->dev, sizeof(*priv),
++ GFP_KERNEL);
++ if (priv == NULL)
++ return -ENOMEM;
++
++ i2c_set_clientdata(client, priv);
++
++ priv->max9867_i2c = client;
++ mutex_init(&priv->lock);
++
++ ret = intel_qrk_max9867_get_chip_rev(client);
++ if (ret >= 0)
++ pr_info("%s: Detected MAX9867 chip revision 0x%02X\n",
++ __func__, ret);
++ else {
++ pr_err("%s: Failed to read MAX9867 chip revision\n", __func__);
++ goto exit;
++ }
++
++ ret = intel_qrk_max9867_init(client);
++ if (ret)
++ goto exit;
++
++ priv->gpio_val = GPIO_AUDIO_DEFAULT;
++ ret = intel_qrk_audio_ctrl_gpio_update(priv);
++ if (ret)
++ goto exit;
++
++ /* Create a char dev interface, providing an ioctl config option */
++ ret = intel_qrk_audio_ctrl_chrdev_init(priv);
++ if (ret)
++ goto exit;
++
++ /* Also create a sysfs interface, providing a cmd line config option */
++ ret = sysfs_create_file(&client->dev.kobj,
++ &dev_attr_audio_switch_mode.attr);
++
++exit:
++ return ret;
++}
++
++static int intel_qrk_max9867_i2c_remove(struct i2c_client *client)
++{
++ struct intel_qrk_audio_ctrl_priv *priv = i2c_get_clientdata(client);
++
++ intel_qrk_audio_ctrl_chrdev_remove(priv);
++
++ sysfs_remove_file(&client->dev.kobj, &dev_attr_audio_switch_mode.attr);
++
++ return 0;
++}
++
++static const struct i2c_device_id intel_qrk_max9867_i2c_id[] = {
++ {"intel-qrk-max9867", 0},
++ {}
++};
++MODULE_DEVICE_TABLE(i2c, intel_qrk_max9867_i2c_id);
++
++/* i2c codec control layer */
++static struct i2c_driver intel_qrk_audio_ctrl_i2c_driver = {
++ .driver = {
++ .name = "intel_qrk_audio_ctrl",
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_max9867_i2c_probe,
++ .remove = intel_qrk_max9867_i2c_remove,
++ .id_table = intel_qrk_max9867_i2c_id,
++};
++
++module_i2c_driver(intel_qrk_audio_ctrl_i2c_driver);
++
++MODULE_AUTHOR("Dan O'Donovan <dan@emutex.com>");
++MODULE_DESCRIPTION("Intel Quark platform audio control driver");
++MODULE_LICENSE("Dual BSD/GPL");
+diff --git a/drivers/platform/x86/quark/intel_qrk_audio_ctrl.h b/drivers/platform/x86/quark/intel_qrk_audio_ctrl.h
+new file mode 100644
+index 0000000..581d0e2
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_audio_ctrl.h
+@@ -0,0 +1,45 @@
++/*
++ * Intel Quark platform audio control driver
++ *
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ *
++ * See intel_qrk_audio_ctrl.c for a detailed description
++ *
++ */
++
++#ifndef __INTEL_QRK_AUDIO_CTRL_H__
++#define __INTEL_QRK_AUDIO_CTRL_H__
++
++#include <linux/module.h>
++
++#define INTEL_QRK_AUDIO_MODE_GSM_ONLY 0x0
++#define INTEL_QRK_AUDIO_MODE_SPKR_ONLY 0x1
++#define INTEL_QRK_AUDIO_MODE_SPKR_MIC 0x3
++#define INTEL_QRK_AUDIO_MODE_GSM_SPKR_MIC 0x5
++
++#define INTEL_QRK_AUDIO_MODE_IOC_GSM_ONLY \
++ _IO('x', INTEL_QRK_AUDIO_MODE_GSM_ONLY)
++#define INTEL_QRK_AUDIO_MODE_IOC_SPKR_ONLY \
++ _IO('x', INTEL_QRK_AUDIO_MODE_SPKR_ONLY)
++#define INTEL_QRK_AUDIO_MODE_IOC_SPKR_MIC \
++ _IO('x', INTEL_QRK_AUDIO_MODE_SPKR_MIC)
++#define INTEL_QRK_AUDIO_MODE_IOC_GSM_SPKR_MIC \
++ _IO('x', INTEL_QRK_AUDIO_MODE_GSM_SPKR_MIC)
++
++#endif /* __INTEL_QRK_AUDIO_CTRL_H__ */
+diff --git a/drivers/platform/x86/quark/intel_qrk_board_data.c b/drivers/platform/x86/quark/intel_qrk_board_data.c
+new file mode 100644
+index 0000000..db7f76a
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_board_data.c
+@@ -0,0 +1,260 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark Legacy Platform Data accessor layer
++ *
++ * Simple Legacy SPI flash access layer
++ *
++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013
++ */
++
++#include <asm/io.h>
++#include <linux/dmi.h>
++#include <linux/errno.h>
++#include <linux/ioport.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++
++#define DRIVER_NAME "board_data"
++#define PFX "MFH: "
++#define SPIFLASH_BASEADDR 0xFFF00000
++#define MFH_OFFSET 0x00008000
++#define PLATFORM_DATA_OFFSET 0x00010000
++#define MTD_PART_OFFSET 0x00050000
++#define MTD_PART_LEN 0x00040000
++#define MFH_PADDING 0x1E8
++#define MFH_MAGIC 0x5F4D4648
++#define FLASH_SIZE 0x00400000
++
++/* MFH types supported @ version #1 */
++#define MFH_ITEM_FW_STAGE1 0x00000000
++#define MFH_ITEM_FW_STAGE1_SIGNED 0x00000001
++#define MFH_ITEM_FW_STAGE2 0x00000003
++#define MFH_ITEM_FW_STAGE2_SIGNED 0x00000004
++#define MFH_ITEM_FW_STAGE2_CONFIG 0x00000005
++#define MFH_ITEM_FW_STAGE2_CONFIG_SIGNED 0x00000006
++#define MFH_ITEM_FW_PARAMS 0x00000007
++#define MFH_ITEM_FW_RECOVERY 0x00000008
++#define MFH_ITEM_FW_RECOVERY_SIGNED 0x00000009
++#define MFH_ITEM_BOOTLOADER 0x0000000B
++#define MFH_ITEM_BOOTLOADER_SIGNED 0x0000000C
++#define MFH_ITEM_BOOTLOADER_CONFIG 0x0000000D
++#define MFH_ITEM_BOOTLOADER_CONFIG_SIGNED 0x0000000E
++#define MFH_ITEM_KERNEL 0x00000010
++#define MFH_ITEM_KERNEL_SIGNED 0x00000011
++#define MFH_ITEM_RAMDISK 0x00000012
++#define MFH_ITEM_RAMDISK_SIGNED 0x00000013
++#define MFH_ITEM_LOADABLE_PROGRAM 0x00000015
++#define MFH_ITEM_LOADABLE_PROGRAM_SIGNED 0x00000016
++#define MFH_ITEM_BUILD_INFO 0x00000018
++#define MFH_ITEM_VERSION 0x00000019
++
++struct intel_qrk_mfh {
++ u32 id;
++ u32 ver;
++ u32 flags;
++ u32 next_block;
++ u32 item_count;
++ u32 boot_priority_list;
++ u8 padding[MFH_PADDING];
++};
++
++struct intel_qrk_mfh_item {
++ u32 type;
++ u32 addr;
++ u32 len;
++ u32 res0;
++};
++
++struct kobject * board_data_kobj;
++EXPORT_SYMBOL_GPL(board_data_kobj);
++
++static long unsigned int flash_version_data;
++static ssize_t flash_version_show(struct kobject *kobj,
++ struct kobj_attribute *attr, char *buf)
++{
++ return snprintf(buf, 12, "%#010lx\n", flash_version_data);
++}
++
++static struct kobj_attribute flash_version_attr =
++ __ATTR(flash_version, 0644, flash_version_show, NULL);
++
++extern int intel_qrk_plat_probe(struct resource * pres);
++
++#define DEFAULT_BOARD "Galileo"
++
++static struct platform_device bsp_data [] = {
++ {
++ .name = "QuarkEmulation",
++ .id = -1,
++ },
++ {
++ .name = "ClantonPeakSVP",
++ .id = -1,
++ },
++ {
++ .name = "KipsBay",
++ .id = -1,
++ },
++ {
++ .name = "CrossHill",
++ .id = -1,
++ },
++ {
++ .name = "ClantonHill",
++ .id = -1,
++ },
++ {
++ .name = "Galileo",
++ .id = -1,
++ },
++
++};
++
++/**
++ * add_firmware_sysfs_entry
++ *
++ * Add an entry in sysfs consistent with Galileo IDE's expected location
++ * covers current software versions and legacy code < Intel Galileo BIOS 0.9.0
++ *
++ */
++static int add_firmware_sysfs_entry(const char * board_name)
++{
++ extern struct kobject * firmware_kobj;
++
++ pr_info("Intel Quark Board %s Firmware Version %#010lx\n",
++ board_name, flash_version_data);
++
++ /* board_data_kobj subordinate of firmware @ /sys/firmware/board_data */
++ board_data_kobj = kobject_create_and_add("board_data", firmware_kobj);
++ if (!board_data_kobj) {
++ pr_err(PFX"kset create error\n");
++ return -ENODEV;
++ }
++ return sysfs_create_file(board_data_kobj, &flash_version_attr.attr);
++}
++
++/**
++ * intel_qrk_board_data_init_legacy
++ *
++ * Module entry point for older BIOS versions
++ * Allows more recent kernels to boot on Galileo boards with BIOS before release
++ * 0.9.0
++ */
++static int __init intel_qrk_board_data_init_legacy(void)
++{
++ struct intel_qrk_mfh __iomem * mfh;
++ struct intel_qrk_mfh_item __iomem * item;
++ struct platform_device * pdev;
++ u32 i;
++ char * board_name = NULL;
++ void __iomem * spi_data;
++ int ret = 0;
++
++ spi_data = ioremap(SPIFLASH_BASEADDR, FLASH_SIZE);
++ if (!spi_data)
++ return -ENODEV;
++
++ /* get mfh and first item pointer */
++ mfh = spi_data + MFH_OFFSET;
++ if (mfh->id != MFH_MAGIC){
++ pr_err(PFX"Bad MFH magic want 0x%08x found 0x%08x @ 0x%p\n",
++ MFH_MAGIC, mfh->id, &mfh->id);
++ return -ENODEV;
++ }
++
++ pr_info(PFX"Booting on an old BIOS assuming %s board\n", DEFAULT_BOARD);
++ pr_info(PFX"mfh @ 0x%p: id 0x%08lx ver 0x%08lx entries 0x%08lx\n",
++ mfh, (unsigned long)mfh->id, (unsigned long)mfh->ver,
++ (unsigned long)mfh->item_count);
++ item = (struct intel_qrk_mfh_item __iomem *)
++ &mfh->padding [sizeof(u32) * mfh->boot_priority_list];
++
++ /* Register a default board */
++ for (i = 0; i < sizeof(bsp_data)/sizeof(struct platform_device); i++){
++ if (!strcmp(bsp_data[i].name, DEFAULT_BOARD)){
++ board_name = (char*)bsp_data[i].name;
++ platform_device_register(&bsp_data[i]);
++ }
++ }
++
++ /* Register flash regions as seperate platform devices */
++ for (i = 0; i < mfh->item_count; i++, item++){
++ pdev = NULL;
++
++ switch (item->type){
++ case MFH_ITEM_VERSION:
++ flash_version_data = item->res0;
++ ret = add_firmware_sysfs_entry(board_name);
++ break;
++ default:
++ break;
++ }
++ }
++ iounmap(spi_data);
++ return ret;
++}
++
++/**
++ * intel_qrk_board_data_init_legacy
++ *
++ * Module entry point for older BIOS versions
++ */
++static int __init intel_qrk_board_data_init(void)
++{
++ bool found = false;
++ const char * bios_version = dmi_get_system_info(DMI_BIOS_VERSION);
++ const char * board_name = dmi_get_system_info(DMI_BOARD_NAME);
++ int ret = 0;
++ u32 i;
++
++ /* BIOS later than version 0.9.0 contains the right DMI data */
++ for (i = 0; board_name != NULL && bios_version != NULL &&
++ i < sizeof(bsp_data)/sizeof(struct platform_device); i++){
++
++ if (!strcmp(bsp_data[i].name, board_name)){
++
++ /* Register board */
++ platform_device_register(&bsp_data[i]);
++ found = true;
++
++ /* Galileo IDE expects this entry */
++ flash_version_data = simple_strtoul(bios_version, NULL, 16);
++ ret = add_firmware_sysfs_entry(bsp_data[i].name);
++
++ break;
++ }
++ }
++
++ /* For older BIOS without DMI data we read the data directly from flash */
++ if (found == false){
++ ret = intel_qrk_board_data_init_legacy();
++ }
++
++ return ret;
++}
++
++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>");
++MODULE_DESCRIPTION("Intel Quark SPI Data API");
++MODULE_LICENSE("Dual BSD/GPL");
++subsys_initcall(intel_qrk_board_data_init);
++
+diff --git a/drivers/platform/x86/quark/intel_qrk_esram.c b/drivers/platform/x86/quark/intel_qrk_esram.c
+new file mode 100644
+index 0000000..55adb41
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_esram.c
+@@ -0,0 +1,1144 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark eSRAM overlay driver
++ *
++ * eSRAM is an on-chip fast access SRAM.
++ *
++ * This driver provides the ability to map a kallsyms derived symbol of
++ * arbitrary length or a struct page entitiy.
++ * A sysfs interface is provided to allow map of kernel structures, without
++ * having to use the API from your code directly.
++ *
++ * Example:
++ * echo idt_table > /sys/devices/intel-qrk-esram.0/map
++ *
++ * An API is provided to allow for mapping of a) kernel symbols or b) pages.
++ * eSRAM requires 4k physically aligned addresses to work - so a struct page
++ * fits neatly into this.
++ *
++ * intel_qrk_esram_map_sym(ohci_irq);
++ * intel_qrk_esram_map_page(virt_to_page(ohci_irq), "ohci_irq");
++ * Are equivalent - with the exception that map_sym() can detect if a mapping
++ * crosses a page-boundary, whereas map_page just maps one page. Generally use
++ * map_sym() for code and map_page() for data
++ *
++ * To populte eSRAM we must copy data to a temporary buffer, overlay and
++ * then copy data back to the eSRAM region.
++ *
++ * When entering S3 - we must save eSRAM state to DRAM, and similarly on restore
++ * to S0 we must repopulate eSRAM
++ * Unmap code is included for reference however the cache coherency of unmap is
++ * not guaranteed so the functionality is not exported by this code
++ *
++ */
++#include <asm/cacheflush.h>
++#include <asm/desc.h>
++#include <asm/io.h>
++#include <asm/pgtable.h>
++#include <asm/special_insns.h>
++#include <asm-generic/uaccess.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/fs.h>
++#include <linux/intel_qrk_sb.h>
++#include <linux/kallsyms.h>
++#include <linux/list.h>
++#include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/printk.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/seq_file.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/timer.h>
++
++#include "intel_qrk_esram.h"
++
++#define DRIVER_NAME "intel-qrk-esram"
++
++/* Shorten fn names to fit 80 char limit */
++#ifndef sb_read
++#define sb_read intel_qrk_sb_read_reg
++#endif
++#ifndef sb_write
++#define sb_write intel_qrk_sb_write_reg
++#endif
++
++/* Define size of pages, ECC scrub demark etc */
++#define MAX_PAGE_RETRIES (100)
++#define MS_PER_HOUR (3600000UL)
++#define ESRAM_PAGE_COUNT INTEL_QRK_ESRAM_PAGE_COUNT
++#define ESRAM_PAGE_MASK (0xFFFFF000)
++#define ESRAM_PAGE_SIZE INTEL_QRK_ESRAM_PAGE_SIZE
++#define ESRAM_TOTAL_SIZE (ESRAM_PAGE_COUNT * ESRAM_PAGE_SIZE)
++#define ECC_MAX_REFRESH_PERIOD (48)
++#define ECC_DEFAULT_REFRESH_PERIOD (24)
++#define ECC_DRAM_READSIZE (512) /* bytes per DRAM ECC */
++#define ECC_ESRAM_READSIZE ESRAM_PAGE_SIZE /* bytes per SRAM ECC */
++
++/* Register ID */
++#define ESRAM_PGPOOL_REG (0x80) /* PGPOOL */
++#define ESRAM_CTRL_REG (0x81) /* ESRAMCTRL */
++#define ESRAM_PGBLOCK_REG (0x82) /* Global page ctrl */
++#define ESCRM_ECCCERR_REG (0x83) /* Correctable ECC */
++#define ESRAM_ECCUCERR_REG (0x84) /* Uncorrectable ECC */
++
++/* Reg commands */
++#define ESRAM_CTRL_READ (0x10) /* Config reg */
++#define ESRAM_CTRL_WRITE (0x11) /* Config reg */
++#define ESRAM_PAGE_READ (0x12) /* Page config read */
++#define ESRAM_PAGE_WRITE (0x13) /* Page config write */
++
++/* ESRAMPGPOOL reg 0x80 - r/w opcodes 0x10/0x11 */
++#define ESRAM_PGPOOL_FLUSHING(x) ((x>>18)&0x1FF)
++#define ESRAM_PGPOOL_PGBUSY(x) ((x>>9)&0x1FF)
++
++/* ESRAMCTRL reg 0x81 - r/w opcodes 0x10/0x11 */
++#define ESRAM_CTRL_FLUSHPRI(x) ((x>>25)&0x03) /* DRAM flush priority */
++#define ESRAM_CTRL_SIZE(x) ((x>>16)&0xFF) /* # of 4k pages */
++#define ESRAM_CTRL_ECCTHRESH(x) ((x>>8)&0xFF) /* ECC threshold */
++#define ESRAM_CTRL_THRESHMSG_EN (0x00000080) /* ECC notification */
++#define ESRAM_CTRL_ISAVAIL (0x00000010) /* ESRAM on die ? */
++#define ESRAM_CTRL_BLOCK_MODE (0x00000008) /* Block mode enable */
++#define ESRAM_CTRL_GLOBAL_LOCK (0x00000004) /* Global lock status */
++#define ESRAM_CTRL_FLUSHDISABLE (0x00000002) /* Global flush/dis */
++#define ESRAM_CTRL_SECDEC (0x00000001) /* ECC enable bit */
++
++/* PGBLOCK reg 0x82 - opcode 0x10/0x11 */
++#define ESRAM_PGBLOCK_FLUSHEN (0x80000000) /* Block flush enable */
++#define ESRAM_PGBLOCK_PGFLUSH (0x40000000) /* Flush the block */
++#define ESRAM_PGBLOCK_DISABLE (0x20000000) /* Block mode disable */
++#define ESRAM_PGBLOCK_ENABLE (0x10000000) /* Block mode enable */
++#define ESRAM_PGBLOCK_LOCK (0x08000000) /* Block mode lock en */
++#define ESRAM_PGBLOCK_INIT (0x04000000) /* Block init in prog */
++#define ESRAM_PGBLOCK_BUSY (0x01000000) /* Block is enabled */
++#define ESRAM_PGBLOCK_SYSADDR(x) (x&0x000000FF)
++
++/* ESRAMPGCTRL - opcode 0x12/0x13 */
++#define ESRAM_PAGE_FLUSH_PAGE_EN (0x80000000) /* S3 autoflush */
++#define ESRAM_PAGE_FLUSH (0x40000000) /* Flush page to DRAM */
++#define ESRAM_PAGE_DISABLE (0x20000000) /* page disable bit */
++#define ESRAM_PAGE_EN (0x10000000) /* Page enable */
++#define ESRAM_PAGE_LOCK (0x08000000) /* Page lock en */
++#define ESRAM_PAGE_INITIALISING (0x04000000) /* Init in progress */
++#define ESRAM_PAGE_BUSY (0x01000000) /* Page busy */
++#define ESRAM_PAGE_MAP_SHIFT (12) /* Shift away 12 LSBs */
++
++/* Extra */
++#define ESRAM_MAP_OP (0x01)
++#define ESRAM_UNMAP_OP (0x00)
++
++/**
++ * struct esram_refname
++ *
++ * Structure to hold a linked list of names
++ */
++struct esram_refname {
++ char name[KSYM_SYMBOL_LEN]; /* Name of mapping */
++ struct list_head list;
++};
++
++/**
++ * struct esram_page
++ *
++ * Represents an eSRAM page in our linked list
++ */
++struct esram_page {
++
++ struct list_head list; /* List entry descriptor */
++ struct list_head name_list; /* Linked list for name references */
++ u32 id; /* Page ID */
++ u32 phys_addr; /* Physial address of page */
++ u32 refcount; /* Reference count */
++ u32 vaddr; /* Virtual address of page */
++
++};
++
++/**
++ * struct intel_qrk_esram_dev
++ *
++ * Structre to represent module state/data/etc
++ */
++struct intel_qrk_esram_dev{
++
++ /* Linux kernel structures */
++ struct list_head page_used; /* Used pages */
++ struct list_head page_free; /* Free pages */
++ spinlock_t slock; /* Spinlock */
++ struct platform_device *pldev; /* Platform device */
++
++ /* Actual data */
++ struct esram_page * pages;
++ u8 cbuf[ESRAM_PAGE_SIZE];
++
++ /* Stats */
++ u32 page_count; /* As reported by silicon */
++ u32 page_disable_retries; /* Aggreate count on disable */
++ u32 page_enable_retries; /* Aggregate spin count page enable */
++ u32 page_free_ct; /* Free pages for mapping code section */
++};
++
++static struct intel_qrk_esram_dev esram_dev;
++
++/*
++ * Kallsyms does not provide data addresses. To map important structures such as
++ * the idt and gdt, we need to frig the lookup with the below. Other entities
++ * can similarly be added. Note we map a page from the given address - anything
++ * larger will require additional code to handle
++ */
++struct esram_symex {
++ char * name;
++ void * vaddr;
++ u32 size;
++};
++
++static struct esram_symex esram_symex[] =
++{
++ {
++ .name = "idt_table",
++ .vaddr = &idt_table,
++ .size = ESRAM_PAGE_SIZE,
++ },
++ {
++ .name = "gdt_page",
++ .vaddr = &gdt_page,
++ .size = ESRAM_PAGE_SIZE,
++ },
++};
++
++/**
++ * intel_qrk_esram_stat_show
++ *
++ * @param dev: pointer to device
++ * @param attr: attribute pointer
++ * @param buf: output buffer
++ * @return number of bytes successfully read
++ *
++ * Populates eSRAM state via /sys/device/intel-qrk-esram.0/stat
++ */
++static ssize_t intel_qrk_esram_stat_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++
++{
++ struct esram_page * epage = NULL;
++ int len = 0;
++ unsigned int count = PAGE_SIZE, size;
++ u32 pgpool = 0, ctrl = 0, pgblock = 0;
++ char * enabled = "enabled";
++ char * disabled = "disabled";
++
++ /* Display page-pool relevant data */
++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_PGPOOL_REG, &pgpool, 1);
++ size = snprintf(buf, count,
++ "esram-pgpool\t\t\t: 0x%08x\n"
++ "esram-pgpool.free\t\t: %u\n"
++ "esram-pgpool.flushing\t\t: %u\n",
++ pgpool, ESRAM_PGPOOL_PGBUSY(pgpool)+1,
++ ESRAM_PGPOOL_FLUSHING(pgpool) + 1);
++ len += size;
++ count -= size;
++
++ /* Display ctrl reg - most of this is of interest */
++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_CTRL_REG, &ctrl, 1);
++ size = snprintf(buf + len, count - len,
++ "esram-ctrl\t\t\t: 0x%08x\n"
++ "esram-ctrl.ecc\t\t\t: %s\n"
++ "esram-ctrl.ecc-theshold\t\t: %u\n"
++ "esram-ctrl.pages\t\t: %u\n"
++ "esram-ctrl.dram-flush-priorityi\t: %u\n",
++ ctrl, (ctrl & ESRAM_CTRL_SECDEC) ? enabled : disabled,
++ ESRAM_CTRL_ECCTHRESH(ctrl), ESRAM_CTRL_SIZE(ctrl)+1,
++ ESRAM_CTRL_FLUSHPRI(ctrl));
++ len += size;
++ count -= size;
++
++ /* Display block ctrl/stat - we should be !block mode */
++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_PGBLOCK_REG, &pgblock, 1);
++ size = snprintf(buf + len, count - len, "esram-block\t\t\t: 0x%08x\n",
++ pgblock);
++ len += size;
++ count -= size;
++
++ /* Print ECC status regs */
++
++ /* Print per-page info */
++ size = snprintf(buf + len, count - len,
++ "free page\t\t\t: %u\nused page\t\t\t: %u\n"
++ "refresh \t\t\t: %ums\npage enable retries\t\t: %u\n"
++ "page disable retries\t: %u\n",
++ esram_dev.page_free_ct,
++ esram_dev.page_count-esram_dev.page_free_ct,
++ 0,
++ esram_dev.page_enable_retries,
++ esram_dev.page_disable_retries);
++ len += size;
++ count -= size;
++
++ spin_lock(&esram_dev.slock);
++ if(!list_empty(&esram_dev.page_free)){
++
++ epage = list_first_entry(&esram_dev.page_free, struct esram_page, list);
++ size = snprintf(buf + len, count - len,
++ "ecc next page \t\t\t: %u\n",epage->id);
++ len += size;
++ count -= size;
++
++
++ }
++ spin_unlock(&esram_dev.slock);
++
++ /* Return len indicate eof */
++ return len;
++}
++
++/**
++ * intel_qrk_esram_map_show
++ *
++ * @param dev: pointer to device
++ * @param attr: attribute pointer
++ * @param buf: output buffer
++ * @return number of bytes successfully read
++ *
++ * Read back eSRAM mapped entries
++ */
++static ssize_t
++intel_qrk_esram_map_show(struct device *dev,struct device_attribute *attr,
++ char *buf)
++{
++ struct esram_page * epage = NULL;
++ struct esram_refname * refname = NULL;
++ int len = 0, size = 0;
++ unsigned int count = PAGE_SIZE;
++
++ spin_lock(&esram_dev.slock);
++ list_for_each_entry(epage, &esram_dev.page_used, list){
++ /* Print references */
++ list_for_each_entry(refname, &epage->name_list, list){
++ size = snprintf(buf + len, count - len,
++ "%s ", refname->name);
++ len += size;
++ count -= size;
++ }
++ /* Print data */
++ size += snprintf(buf + len, count - len,
++ "\n\tPage virt 0x%08x phys 0x%08x\n"
++ "\tRefcount %u\n",
++ epage->vaddr, epage->phys_addr,
++ epage->refcount);
++ len += size;
++ count -= size;
++ }
++ spin_unlock(&esram_dev.slock);
++
++ /* Return len indicate eof */
++ return len;
++}
++
++/**
++ * intel_qrk_esram_map_store
++ *
++ * @param dev: pointer to device
++ * @param attr: attribute pointer
++ * @param buf: input buffer
++ * @param size: size of input data
++ * @return number of bytes successfully written
++ *
++ * Function allows user-space to switch mappings on/off with a simple
++ * echo idt_table > /sys/devices/intel-qrk-esram.0/map type command
++ */
++static ssize_t
++intel_qrk_esram_map_store(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ ssize_t ret = 0;
++ char * sbuf = NULL;
++ unsigned long vaddr = 0, i = 0;
++ unsigned int count = PAGE_SIZE;
++
++ if(count <= 1){
++ return -EINVAL;
++ }
++
++ /* Get input */
++ sbuf = (char*)buf;
++
++ /* Fixup entity to scrub spaces */
++ while(sbuf < (buf + count)){
++ if(*sbuf == ' ' || *sbuf == '\r' || *sbuf =='\n'){
++ *sbuf = 0;
++ break;
++ }
++ sbuf++;
++ }
++
++ /* Check to see if we are being asked to map a non-kallsyms addr */
++ for(i = 0; i < sizeof(esram_symex)/sizeof(struct esram_symex); i++){
++ if(strcmp(buf, esram_symex[i].name) == 0){
++ ret = intel_qrk_esram_map_range(
++ esram_symex[i].vaddr,
++ esram_symex[i].size,
++ esram_symex[i].name);
++ goto done;
++ }
++ }
++
++ /* This path relies on kallsyms to provide name/address data */
++ vaddr = kallsyms_lookup_name(buf);
++ if(vaddr == 0)
++ goto done;
++
++ ret = intel_qrk_esram_map_symbol((void*)vaddr);
++done:
++ if(ret == 0)
++ ret = (ssize_t)count;
++ return ret;
++}
++
++static struct device_attribute dev_attr_stats = {
++ .attr = {
++ .name = "stats",
++ .mode = 0444,
++ },
++ .show = intel_qrk_esram_stat_show,
++};
++
++static struct device_attribute dev_attr_map = {
++ .attr = {
++ .name = "map",
++ .mode = 0644,
++ },
++ .show = intel_qrk_esram_map_show,
++ .store = intel_qrk_esram_map_store,
++};
++
++static struct attribute *platform_attributes[] = {
++ &dev_attr_stats.attr,
++ &dev_attr_map.attr,
++ NULL,
++};
++
++static struct attribute_group esram_attrib_group = {
++ .attrs = platform_attributes
++};
++
++/******************************************************************************
++ * eSRAM Core
++ ******************************************************************************/
++
++/**
++ * intel_qrk_esram_page_busy
++ *
++ * @param epage: Pointer to the page descriptor
++ * @return boolean indicating whether or not a page is enabled
++ */
++static int intel_qrk_esram_page_busy(struct esram_page * epage, u8 lock)
++{
++ u32 reg = 0;
++
++ sb_read(SB_ID_ESRAM, ESRAM_PAGE_READ, epage->id, &reg, lock);
++ return (reg&(ESRAM_PAGE_BUSY | ESRAM_PAGE_FLUSH | ESRAM_PAGE_DISABLE));
++}
++
++/**
++ * intel_qrk_esram_fault
++ *
++ * Dump eSRAM registers and kernel panic
++ * Nothing else to do at this point
++ */
++void intel_qrk_esram_fault(struct esram_page * epage, u32 lineno)
++{
++ u32 reg = 0, next = 0, prev = 0, prev_reg = 0;
++ u32 next_reg = 0, block = 0, ctrl = 0;
++
++ pr_err("eSRAM: fault @ %s:%d\n", __FILE__, lineno);
++ sb_read(SB_ID_ESRAM, ESRAM_PAGE_READ, epage->id, &reg, 1);
++ pr_err("read page %d state 0x%08x\n", epage->id, reg);
++ if(epage->id == 0){
++ next = 1; prev = 127;
++ }else if(epage->id == 127){
++ next = 0; prev = 126;
++ }else{
++ next = epage->id+1;
++ prev = epage->id-1;
++ }
++ sb_read(SB_ID_ESRAM, ESRAM_PAGE_READ, next, &next_reg, 1);
++ sb_read(SB_ID_ESRAM, ESRAM_PAGE_READ, prev, &prev_reg, 1);
++
++ /* Get state */
++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_CTRL_REG, &ctrl, 1);
++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_PGBLOCK_REG, &block, 1);
++
++ pr_err("eSRAM: CTRL 0x%08x block 0x%08x\n", ctrl, block);
++ pr_err("Prev page %d state 0x%08x Next page %d state 0x%08x\n"
++ , next, next_reg, prev, prev_reg);
++ BUG();
++}
++
++
++/**
++ * intel_qrk_esram_page_enable
++ *
++ * @param epage: struct esram_page carries data to program to register
++ * @param lock: Indicates whether to attain sb spinlock or not
++ *
++ * Enable an eSRAM page spinning for page to become ready.
++ */
++static void intel_qrk_esram_page_enable(struct esram_page *epage, u8 lock)
++{
++ u32 ret = 0;
++
++ /* Fault if we try to enable a disabled page */
++ if(intel_qrk_esram_page_busy(epage, lock)){
++ intel_qrk_esram_fault(epage, __LINE__);
++ }
++
++ /* Program page mapping */
++ sb_write(SB_ID_ESRAM, ESRAM_PAGE_WRITE, epage->id,
++ ESRAM_PAGE_FLUSH_PAGE_EN | ESRAM_PAGE_EN |
++ (epage->phys_addr>>ESRAM_PAGE_MAP_SHIFT), lock);
++ do {
++ /* Poll until page busy bit becomes true */
++ ret = intel_qrk_esram_page_busy(epage, lock);
++
++ /* This branch should rarely if ever be true */
++ if(unlikely(ret == 0)){
++ esram_dev.page_enable_retries++;
++ }
++
++ }while(ret == 0);
++}
++
++/**
++ * intel_qrk_esram_page_disable_sync
++ *
++ * @param epage: pointer to eSRAM page descriptor
++ *
++ * This function spins waiting for disable bit to clear, useful right after a
++ * disable/disable-flush command. Interrupts are enabled here, sleeping is OK
++ */
++static void intel_qrk_esram_page_disable_sync(struct esram_page * epage)
++{
++ u32 ret = 0, retries = 0;
++ do {
++ /* Poll for busy bit clear */
++ ret = intel_qrk_esram_page_busy(epage, 1);
++
++ /* This branch should rarely if ever be true */
++ if(unlikely(ret)){
++ esram_dev.page_disable_retries++;
++ retries++;
++ }
++
++ if(retries == MAX_PAGE_RETRIES){
++ intel_qrk_esram_fault(epage, __LINE__);
++ }
++ }while(ret);
++}
++
++/**
++ * intel_qrk_esram_page_disable
++ *
++ * @param epage: struct esram_page carries data to program to register
++ *
++ * Disable the eSRAM page no flush. Interrupts are enabled here, sleeping is OK
++ */
++static void intel_qrk_esram_page_disable(struct esram_page *epage)
++{
++ sb_write(SB_ID_ESRAM, ESRAM_PAGE_WRITE, epage->id,
++ ESRAM_PAGE_DISABLE, 1);
++ intel_qrk_esram_page_disable_sync(epage);
++}
++
++/**
++ * intel_qrk_esram_page_flush_disable
++ *
++ * @param epage: struct esram_page carries data to program to register
++ *
++ * Disable the eSRAM page - with flush. Note the architecture will block access
++ * to the overlayed region until the flush has completed => irqs may be switched
++ * on during this operation.
++ */
++static void intel_qrk_esram_page_flush_disable(struct esram_page *epage)
++{
++
++
++ /* Do flush */
++ sb_write(SB_ID_ESRAM, ESRAM_PAGE_WRITE, epage->id,
++ ESRAM_PAGE_FLUSH | ESRAM_PAGE_DISABLE, 1);
++
++ intel_qrk_esram_page_disable_sync(epage);
++}
++
++#if 0
++/**
++ * intel_qrk_esram_flush_disable_all
++ *
++ * Flushes and disables all enabled eSRAM pages
++ */
++static void intel_qrk_esram_page_flush_disable_all(void)
++{
++ struct esram_page * epage = NULL;
++
++ spin_lock(&esram_dev.slock);
++ list_for_each_entry(epage, &esram_dev.page_used, list){
++ intel_qrk_esram_page_flush_disable(epage);
++ }
++ spin_unlock(&esram_dev.slock);
++}
++#endif
++
++/**
++ * intel_qrk_esram_page_populate_atomic
++ *
++ * @param epage: Pointer to eSRAM page desciptor.
++ * @return 0 placeholder, later versions may return error
++ *
++ * Function takes the mappings given in epage and uses the values to populate
++ * an eSRAM page. The copy/enable/copy routine must be done atomically, since we
++ * may be doing a memcpy() of an ISR for example.
++ * For this reason we wrapper this entire call into a callback provided by
++ * side-band, which does a spin_lock_irqsave calls this function and then does
++ * a spin_lock_irqrestore - thus guaranteeing atomicity of the below code and
++ * respect for the locking strategy of the side-band driver
++ */
++static int intel_qrk_esram_page_populate_atomic(struct esram_page * epage)
++{
++ unsigned long crz;
++
++ /* Copy away */
++ memcpy(&esram_dev.cbuf, (void*)epage->vaddr, ESRAM_PAGE_SIZE);
++
++ /* If CR0.WP is true - flip it HSD # 4930660 */
++ crz = read_cr0();
++ if (crz & X86_CR0_WP){
++ write_cr0(crz & (~X86_CR0_WP));
++ }
++
++ /* Disable NMI */
++ outb(0x80, 0x70);
++
++ /* Enable page mapping */
++ intel_qrk_esram_page_enable(epage, 0);
++
++ /* Copy back - populating memory overlay */
++ memcpy((void*)epage->vaddr, &esram_dev.cbuf, ESRAM_PAGE_SIZE);
++
++ /* Re-enable NMI */
++ outb(0x00, 0x70);
++
++ /* Restore CR0.WP if appropriate HSD # 4930660 */
++ if (crz & X86_CR0_WP){
++ write_cr0(crz);
++ }
++ return 0;
++}
++
++/**
++ * intel_qrk_esram_page_populate
++ *
++ * @param epage: Pointer to eSRAM page desciptor.
++ * @return 0 on success < 0 on failure
++ *
++ * Populates the page. set_memory_rw/set_memory_ro require local irqs enabled.
++ * intel_qrk_esram_page_populate_atomic - needs irqs switched off since memory
++ * can be inconsistent during the populate operation. Depopulate operations are
++ * architecturally guaranteed
++ */
++static int intel_qrk_esram_page_populate(struct esram_page * epage)
++{
++ int flip_rw = 0, level = 0, ret = 0;
++ pte_t * pte = epage != NULL ? lookup_address(epage->vaddr, &level):NULL;
++
++ if(unlikely(pte == NULL)){
++ return -EINVAL;
++ }
++
++ /* Determine if we need to set writable */
++ flip_rw = !(pte_write(*pte));
++
++ /* Ensure memory is r/w - do so before spin_lock_irqsave */
++ if(flip_rw){
++ ret = set_memory_rw(epage->vaddr, 1);
++ if (ret != 0){
++ pr_err("%s error during set_memory_rw = %d\n",
++ __func__, ret);
++ return ret;
++ }
++ }
++
++ /* Force ECC update @ disable only */
++ intel_qrk_esram_page_enable(epage, 1);
++ intel_qrk_esram_page_disable(epage);
++
++ /* Enable and populate eSRAM page using callback in sb with irqs off */
++ ret |= intel_qrk_sb_runfn_lock(
++ (int (*)(void*))intel_qrk_esram_page_populate_atomic,(void*)epage);
++
++ /* If we set memory writable - restore previous state */
++ if(flip_rw){
++ ret |= set_memory_ro(epage->vaddr, 1);
++ if (ret != 0){
++ pr_err("%s error during set_memory_ro = %d\n",
++ __func__, ret);
++ return ret;
++ }
++ }
++
++ return ret;
++}
++/**
++ * intel_qrk_esram_page_addref
++ *
++ * @param epage: eSRAM page descriptor
++ * @param name: Name of reference to add
++ * @return zero on success negative on error
++ *
++ */
++static int intel_qrk_esram_page_addref(struct esram_page * epage, char * name)
++{
++ struct esram_refname * refname = NULL;
++ if(unlikely(epage == NULL || name == NULL)){
++ return -EINVAL;
++ }
++
++ refname = kzalloc(sizeof(struct esram_refname), GFP_KERNEL);
++ if(unlikely(refname == NULL)){
++ return -ENOMEM;
++ }
++
++ /* Add to list */
++ strncpy(refname->name, name, sizeof(refname->name));
++ list_add(&refname->list, &epage->name_list);
++
++ /* Bump reference count */
++ epage->refcount++;
++ return 0;
++}
++
++
++/**
++ * __intel_qrk_esram_map_page
++ *
++ * @param page: Page to map
++ * @param name: Name of the mapping
++ * @return 0 success < 0 failure
++ *
++ * Overlay a vritual address rangne eeds to be aligned to a 4k address.
++ * Since multiple items can live in a 4k range, it is possible when calling
++ * into map_page() that a previous mapping will have already covered some or all
++ * of the mapping we want. This is not an error case, if the map function finds
++ * it is being asked to map a 4k range already mapped it returns 0, to indicate
++ * the mapping has suceeded i.e. it's already been mapped. This is logical if
++ * you think about it. In contrast being asked to unmap a region not mapped is
++ * clearly an error...
++ *
++ */
++static int __intel_qrk_esram_map_page(u32 vaddr, char * name)
++{
++ int ret = 0;
++ struct esram_page * epage = NULL;
++ struct esram_refname * refname = NULL;
++
++ if(unlikely(name == NULL)){
++ return -EINVAL;
++ }
++
++ if(unlikely(esram_dev.page_free_ct == 0)){
++ return -ENOMEM;
++ }
++
++ /* Verify if we have already mapped */
++ list_for_each_entry(epage, &esram_dev.page_used, list){
++ if(epage->vaddr == vaddr){
++
++ /* Page already mapped */
++ list_for_each_entry(refname, &epage->name_list, list){
++ if(strcmp(refname->name, name)==0){
++ /* Page mapped at this name */
++ return -EINVAL;
++ }
++ }
++ /* New symbol in previous mapping */
++ return intel_qrk_esram_page_addref(epage, name);
++ }
++ }
++
++ /* Enumerate eSRAM page structure */
++ epage = list_first_entry(&esram_dev.page_free, struct esram_page, list);
++ epage->phys_addr = virt_to_phys((void*)vaddr);
++ epage->vaddr = vaddr;
++ ret = intel_qrk_esram_page_addref(epage, name);
++ if(unlikely(ret < 0)){
++ return ret;
++ }
++
++ /* Populate page */
++ ret = intel_qrk_esram_page_populate(epage);
++
++ /* Move to used list */
++ list_move(&epage->list, &esram_dev.page_used);
++ esram_dev.page_free_ct--;
++
++ return ret;
++}
++
++/**
++ * __intel_qrk_esram_unmap_page
++ *
++ * @param page: Page to unmap
++ * @param name: Name of the mapping
++ * @return 0 success < 0 failure
++ *
++ * Unmap a previously mapped virutal address range.
++ * Must be 4k aligned
++ *
++ */
++static int __intel_qrk_esram_unmap_page(u32 vaddr, char * name)
++{
++ u8 found = 0;
++ struct esram_page * epage = NULL;
++ struct esram_refname * refname = NULL;
++
++ /* Find physical address */
++ list_for_each_entry(epage, &esram_dev.page_used, list){
++ if(epage->vaddr == vaddr){
++ found = 1;
++ break;
++ }
++ }
++
++ /* Bail out on error */
++ if(found == 0){
++ pr_err("0x%08x not mapped\n", vaddr);
++ return -EINVAL;
++ }
++
++ /* Determine reference to delete */
++ found = 0;
++ list_for_each_entry(refname, &epage->name_list, list){
++ if(strcmp(refname->name,name)==0){
++ found = 1;
++ break;
++ }
++ }
++ if(unlikely(found == 0)){
++ pr_err("No mapping %s!\n", name);
++ return -EINVAL;
++ }
++
++ /* Remove entry decrement reference count */
++ list_del(&refname->list);
++ kfree(refname);
++ if(--epage->refcount > 0){
++ return 0;
++ }
++
++ /* Flush and disable page */
++ intel_qrk_esram_page_flush_disable(epage);
++
++ /* Move to free list tail - scrub entries come from head */
++ list_move_tail(&epage->list, &esram_dev.page_free);
++ esram_dev.page_free_ct++;
++
++ return 0;
++}
++
++/**
++ *
++ * __intel_qrk_esram_page_op
++ *
++ * @param vaddr: Virtual address of symbol
++ * @param size: Size/length of symbol
++ * @param name: Name of mapping
++ * @param map: Boolean indicates whether to map or unmap the page
++ * @return 0 success < 0 failure
++ *
++ * This function maps/unmaps a pages/pages given at the given vaddr. If
++ * the extent of the symbol @ vaddr crosses a page boundary, then we map
++ * multiple pages. Other stuff inside the page, gets a performance boost 'for
++ * free'. Any other data in the page that crosses the physical page boundary
++ * will be partially mapped.
++ */
++static int __intel_qrk_esram_page_op(u32 vaddr, u32 size, char *name, u8 map)
++{
++ unsigned long offset = 0, page_offset = 0;
++ u32 pages = size/ESRAM_PAGE_SIZE + ((size%ESRAM_PAGE_SIZE) ? 1 : 0);
++ int ret = 0;
++
++ /* Compare required pages to available pages */
++ if(map == ESRAM_MAP_OP){
++ if(pages > esram_dev.page_free_ct)
++ return -ENOMEM;
++ }else{
++ if(pages > esram_dev.page_count - esram_dev.page_free_ct)
++ return -ENOMEM;
++ }
++
++ /* Align to 4k and iterate the mappings */
++ vaddr = vaddr&ESRAM_PAGE_MASK;
++ while(size > 0){
++
++ /* Map the page */
++ spin_lock(&esram_dev.slock);
++ if(map == ESRAM_MAP_OP){
++ ret = __intel_qrk_esram_map_page(vaddr, name);
++
++ }else{
++ ret = __intel_qrk_esram_unmap_page(vaddr, name);
++ }
++ spin_unlock(&esram_dev.slock);
++ if(unlikely(ret != 0)){
++ break;
++ }
++
++ /* Calc appropriate offsets */
++ page_offset = offset_in_page(vaddr);
++ if(page_offset + size > ESRAM_PAGE_SIZE){
++
++ offset = ESRAM_PAGE_SIZE - page_offset;
++ size -= offset;
++ vaddr += ESRAM_PAGE_SIZE;
++
++ }else{
++ size = 0;
++ }
++ }
++
++ return ret;
++}
++
++/******************************************************************************
++ * eSRAM API
++ ******************************************************************************/
++
++/**
++ * intel_qrk_esram_map_range
++ *
++ * @param vaddr: Virtual address to start mapping (must be 4k aligned)
++ * @param size: Size to map from
++ * @param mapname: Mapping name
++ * @return 0 success < 0 failure
++ *
++ * Map 4k increments at given address to eSRAM.
++ */
++int intel_qrk_esram_map_range(void * vaddr, u32 size, char * mapname)
++{
++ if(size == 0 || mapname == NULL || vaddr == NULL){
++ return -EINVAL;
++ }
++ return __intel_qrk_esram_page_op((u32)vaddr, size, mapname, ESRAM_MAP_OP);
++}
++EXPORT_SYMBOL(intel_qrk_esram_map_range);
++
++/**
++ * intel_qrk_esram_map_symbol
++ *
++ * @param vaddr: Virtual address of the symbol
++ * @return 0 success < 0 failure
++ *
++ * Maps a series of 4k chunks starting at vaddr&0xFFFFF000. vaddr shall be a
++ * kernel text section symbol (kernel or loaded module)
++ *
++ * We get the size of the symbol from kallsyms. We guarantee to map the entire
++ * size of the symbol - plus whatever padding is necessary to get alignment to
++ * eSRAM_PAGE_SIZE
++ * Other stuff inside the mapped pages will get a performance boost 'for free'.
++ * If this free boost is not what you want then
++ *
++ * 1. Align to 4k
++ * 2. Pad to 4k
++ * 3. Call intel_qrk_esram_map_range()
++ */
++int intel_qrk_esram_map_symbol(void * vaddr)
++{
++ long unsigned int size = 0, offset = 0;
++ char symname[KSYM_SYMBOL_LEN];
++
++ kallsyms_lookup_size_offset((long unsigned int)vaddr, &size, &offset);
++ if(size == 0){
++ return -EINVAL;
++ }
++ sprint_symbol(symname, (u32)vaddr);
++
++ return __intel_qrk_esram_page_op((u32)vaddr, size, symname, 1);
++}
++EXPORT_SYMBOL(intel_qrk_esram_map_symbol);
++
++/******************************************************************************
++ * Module/PowerManagement hooks
++ ******************************************************************************/
++
++/**
++ * intel_qrk_esram_suspend
++ *
++ * @param pdev: Platform device structure (unused)
++ * @param pm: Power managment descriptor
++ * @return 0 success < 0 failure
++ *
++ * For each enabled page - flush to DRAM and disable eSRAM page.
++ * For each 4k region the architecture guarantees atomicity of flush/disable.
++ * Hence any memory transactions to the affected region will stall until
++ * flush/disable completes - hence interrupts are left on.
++ */
++static int intel_qrk_esram_suspend(struct device * pdev)
++{
++ /* Flush and disable of eSRAM pages is carried out automatically */
++ return 0;
++}
++
++/**
++ * intel_qrk_esram_resume
++ *
++ * @param pm: Power management descriptor
++ * @return 0 success < 0 failure
++ *
++ * Runs after resume_noirq. Switches pages back to ro, if appropriate. We do
++ * this here since interrupts will be on, as required by the function
++ * set_memory_ro. If it were possible to set memory ro in resume_noirq we would
++ * do it there instead
++ */
++static int intel_qrk_esram_resume(struct device * pdev)
++{
++ struct esram_page * epage = NULL;
++ int ret = 0;
++
++ list_for_each_entry(epage, &esram_dev.page_used, list){
++ ret |= intel_qrk_esram_page_populate(epage);
++ }
++
++ return ret;
++}
++
++
++/**
++ * intel_qrk_esram_probe
++ *
++ * @param pdev: Platform device
++ * @return 0 success < 0 failure
++ *
++ * Callback from platform sub-system to probe
++ *
++ * This driver manages eSRAM on a per-page basis. Therefore if we find block
++ * mode is enabled, or any global, block-level or page-level locks are in place
++ * at module initialisation time - we bail out.
++ */
++static int intel_qrk_esram_probe(struct platform_device * pdev)
++{
++ int ret = 0;
++ u32 block = 0, ctrl = 0, i = 0, pgstat = 0;
++
++ memset(&esram_dev, 0x00, sizeof(esram_dev));
++ INIT_LIST_HEAD(&esram_dev.page_used);
++ INIT_LIST_HEAD(&esram_dev.page_free);
++ spin_lock_init(&esram_dev.slock);
++ esram_dev.page_free_ct = 0;
++
++ /* Ensure block mode disabled */
++ block = ESRAM_PGBLOCK_DISABLE;
++ sb_write(SB_ID_ESRAM, ESRAM_CTRL_WRITE, ESRAM_PGBLOCK_REG, block, 1);
++
++ /* Get state */
++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_CTRL_REG, &ctrl, 1);
++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_PGBLOCK_REG, &block, 1);
++
++ /* Verify state is good to go */
++ if (ctrl & ESRAM_CTRL_GLOBAL_LOCK){
++ pr_err ("eSRAM: global lock @ 0x%08x\n", ctrl);
++ return -ENODEV;
++ }
++
++ if (block & (ESRAM_PGBLOCK_LOCK | ESRAM_PGBLOCK_ENABLE)){
++ pr_err ("eSRAM: lock @ 0x%08x\n", block);
++ return -ENODEV;
++ }
++ pr_info("eSRAM: CTRL 0x%08x block 0x%08x\n", ctrl, block);
++
++ /* Calculate # of pages silicon supports */
++ esram_dev.page_count = ESRAM_CTRL_SIZE(ctrl) + 1;
++ esram_dev.page_free_ct = esram_dev.page_count;
++ pr_info("eSRAM: pages %d\n", esram_dev.page_free_ct);
++
++ if(esram_dev.page_free_ct <= 1){
++ pr_err("Too few pages reported by eSRAM sub-system\n");
++ return -ENOMEM;
++ }
++
++ /* Allocate an appropriate number of pages */
++ esram_dev.pages = kzalloc(esram_dev.page_count *
++ sizeof(struct esram_page), GFP_KERNEL);
++ if (esram_dev.pages == NULL){
++ return -ENOMEM;
++ }
++
++ /* Initialise list of free pages, explicitely disable as we go */
++ for(i = 0; i < esram_dev.page_count; i++){
++ INIT_LIST_HEAD(&esram_dev.pages[i].name_list);
++ esram_dev.pages[i].id = i;
++
++ /* Read & verify page state */
++ sb_read(SB_ID_ESRAM, ESRAM_PAGE_READ, i, &pgstat, 1);
++ if(pgstat & (ESRAM_PAGE_BUSY | ESRAM_PAGE_LOCK)){
++ pr_err("eSRAM: page %d state 0x%08x err\n", i, pgstat);
++ ret = -ENODEV;
++ goto err;
++ }
++
++ list_add(&esram_dev.pages[i].list, &esram_dev.page_free);
++ }
++
++ ret = sysfs_create_group(&pdev->dev.kobj, &esram_attrib_group);
++ if (ret)
++ goto err;
++
++ return 0;
++err:
++ kfree(esram_dev.pages);
++ return ret;
++}
++
++/*
++ * Power management operations
++ */
++static const struct dev_pm_ops intel_qrk_esram_pm_ops = {
++ .suspend = intel_qrk_esram_suspend,
++ .resume = intel_qrk_esram_resume,
++};
++
++/*
++ * Platform structures useful for interface to PM subsystem
++ */
++static struct platform_driver intel_qrk_esram_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ .pm = &intel_qrk_esram_pm_ops,
++ },
++ .probe = intel_qrk_esram_probe,
++};
++
++module_platform_driver(intel_qrk_esram_driver);
++
++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>");
++MODULE_DESCRIPTION("Intel Quark eSRAM overlay/ECC-scrub driver");
++MODULE_LICENSE("Dual BSD/GPL");
++
+diff --git a/drivers/platform/x86/quark/intel_qrk_esram.h b/drivers/platform/x86/quark/intel_qrk_esram.h
+new file mode 100644
+index 0000000..71aaba1
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_esram.h
+@@ -0,0 +1,107 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark eSRAM overlay driver
++ *
++ * eSRAM is an on-chip fast access SRAM.
++ *
++ * This driver provides the ability to map a kallsyms derived symbol of
++ * arbitrary length or a struct page entitiy.
++ * A proc interface is provided to allow map/unmap of kernel structures, without
++ * having to use the API from your code directly.
++ *
++ * Example:
++ * echo ehci_irq on > /proc/driver/esram/map
++ * echo ehci_irq off > /proc/driver/esram/map
++ *
++ * An API is provided to allow for mapping of a) kernel symbols or b) pages.
++ * eSRAM requires 4k physically aligned addresses to work - so a struct page
++ * fits neatly into this.
++ *
++ * To populte eSRAM we must copy data to a temporary buffer, overlay and
++ * then copy data back to the eSRAM region.
++ *
++ * When entering S3 - we must save eSRAM state to DRAM, and similarly on restore
++ * to S0 we must repopulate eSRAM
++ *
++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>
++ */
++#ifndef __INTEL_QRK_ESRAM_H__
++#define __INTEL_QRK_ESRAM_H__
++
++#include <linux/module.h>
++
++/* Basic size of an eSRAM page */
++#define INTEL_QRK_ESRAM_PAGE_SIZE (0x1000)
++#define INTEL_QRK_ESRAM_PAGE_COUNT (0x80)
++/**
++ * intel_qrk_esram_map_range
++ *
++ * @param vaddr: Virtual address to start mapping (must be 4k aligned)
++ * @param size: Size to map from
++ * @param mapname: Mapping name
++ * @return 0 success < 0 failure
++ *
++ * Map 4k increments at given address to eSRAM.
++ */
++int intel_qrk_esram_map_range(void * vaddr, u32 size, char * mapname);
++
++/**
++ * intel_qrk_esram_unmap_range
++ *
++ * @param vaddr: The virtual address to unmap
++ * @return 0 success < 0 failure
++ *
++ * Logical corollary of esram_map_page
++ */
++int intel_qrk_esram_unmap_range(void * vaddr, u32 size, char * mapname);
++
++/**
++ * intel_qrk_esram_map_symbol
++ *
++ * @param vaddr: Virtual address of the symbol
++ * @return 0 success < 0 failure
++ *
++ * Maps a series of 4k chunks starting at vaddr&0xFFFFF000. vaddr shall be a
++ * kernel text section symbol (kernel or loaded module)
++ *
++ * We get the size of the symbol from kallsyms. We guarantee to map the entire
++ * size of the symbol - plus whatever padding is necessary to get alignment to
++ * eSRAM_PAGE_SIZE
++ * Other stuff inside the mapped pages will get a performance boost 'for free'.
++ * If this free boost is not what you want then
++ * 1. Align to 4k
++ * 2. Pad to 4k
++ * 3. Call intel_qrk_esram_map_range()
++ */
++int intel_qrk_esram_map_symbol(void * vaddr);
++
++/**
++ * intel_qrk_esram_unmap_symbol
++ *
++ * @param vaddr: Virtual address of the symbol
++ * @return 0 success < 0 failure
++ *
++ * Logical corollary to intel_qrk_esram_map_symbol
++ * Undoes any mapping of pages starting at sym for sym's size
++ */
++int intel_qrk_esram_unmap_symbol(void * vaddr);
++
++#endif /* __INTEL_QRK_ESRAM_H__ */
+diff --git a/drivers/platform/x86/quark/intel_qrk_esram_test.c b/drivers/platform/x86/quark/intel_qrk_esram_test.c
+new file mode 100644
+index 0000000..544ad57
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_esram_test.c
+@@ -0,0 +1,602 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/**
++ * intel_qrk_esram_test.c
++ *
++ * Simple test module to provide test cases for ITS integration
++ *
++ */
++#include <linux/cdev.h>
++#include <linux/crc32.h>
++#include <linux/crc32c.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/fs.h>
++#include <linux/intel_qrk_sb.h>
++#include <linux/kallsyms.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/slab.h>
++
++#include "intel_qrk_esram.h"
++#include "intel_qrk_esram_test.h"
++
++#define DRIVER_NAME "intel_qrk_esram_test"
++
++/**
++ * struct intel_qrk_esram_dev
++ *
++ * Structre to represent module state/data/etc
++ */
++struct intel_qrk_esram_test_dev{
++ unsigned int opened;
++ struct platform_device *pldev; /* Platform device */
++ struct cdev cdev;
++ struct mutex open_lock;
++ char * pdata;
++ u32 size;
++};
++
++static struct intel_qrk_esram_test_dev esram_test_dev;
++static struct class *esram_test_class;
++static DEFINE_MUTEX(esram_test_mutex);
++static int esram_test_major;
++static char * name = "testmap";
++
++/******************************************************************************
++ * eSRAM BIST
++ ******************************************************************************/
++
++static int crc_cache = 0;
++
++unsigned long long tsc_delta(unsigned long long first, unsigned long long end)
++{
++ if (first < end)
++ return end - first;
++ else
++ return (ULLONG_MAX - first) + end;
++}
++
++
++/**
++ * intel_qrk_crctest
++ *
++ * Do a CRC32 of the specified region. Return the time taken in jiffies
++ */
++static unsigned long long intel_qrk_crctest(char * pdata, u32 crcsize)
++{
++ unsigned long long j1 = 0, j2 = 0;
++
++ rdtscll(j1);
++
++ /* Flush LMT cache to introduce cache miss to our test */
++ __asm__ __volatile__("wbinvd\n");
++ crc32(0, pdata, crcsize);
++
++ rdtscll(j2);
++
++ return tsc_delta(j1, j2);
++}
++
++#ifdef __DEBUG__
++#define bist_err(x){\
++ pr_err("eSRAM bist err line %d errno %d\n", (__LINE__-2), x);\
++ return x;\
++}
++#else
++#define bist_err(x){\
++ return x;\
++}
++#endif
++/**
++ * intel_qrk_esram_perpage_overlay
++ *
++ * Maps to integration test spec ID CLN.F.SW.APP.eSRAM.0
++ */
++int intel_qrk_esram_test_perpage_overlay(void)
++{
++
++ int ret = 0;
++ u32 idx = 0, size = INTEL_QRK_ESRAM_PAGE_SIZE;
++
++ /* Set a known state */
++ for(idx = 0; idx < size; idx += sizeof(u32)){
++ *((u32*)&esram_test_dev.pdata[idx]) = idx;
++ }
++
++
++ /* Basic test of full range of memory */
++ ret = intel_qrk_esram_map_range(esram_test_dev.pdata, size, name);
++ if(ret){
++ bist_err(ret);
++ }
++ for(idx = 0; idx < size; idx += sizeof(u32)){
++ if(*((u32*)&esram_test_dev.pdata[idx]) != idx){
++ pr_err("Entry %d is 0x%08x require 0x%08x",
++ idx, esram_test_dev.pdata[idx], idx);
++ bist_err(-EIO);
++ }
++ }
++
++#if 0
++ ret = intel_qrk_esram_unmap_range(esram_test_dev.pdata, size, name);
++ if(ret){
++ bist_err(ret);
++ }
++#endif
++ return 0;
++}
++
++/**
++ * intel_qrk_esram_test_pageref_count
++ *
++ * Ensure page reference couting works as expected
++ */
++int intel_qrk_esram_test_pagref_count(void)
++{
++ u32 size = INTEL_QRK_ESRAM_PAGE_SIZE;
++ int ret = 0;
++
++ return 0;
++ /* Map a page */
++ ret = intel_qrk_esram_map_range(esram_test_dev.pdata, size, name);
++ if(ret){
++ bist_err(ret);
++ }
++
++ /* Map a second time - and verify mapping fails */
++ ret = intel_qrk_esram_map_range(esram_test_dev.pdata, size, name);
++ if(ret == 0){
++ bist_err(-EFAULT);
++ }
++
++#if 0
++ /* Unmap - OK */
++ ret = intel_qrk_esram_unmap_range(esram_test_dev.pdata, size, name);
++ if(ret){
++ bist_err(ret);
++ }
++
++ /* Verify second unmap operation fails */
++ ret = intel_qrk_esram_unmap_range(esram_test_dev.pdata, size, name);
++ if(ret == 0){
++ bist_err(-EFAULT);
++ }
++#endif
++ return 0;
++}
++
++extern uint32_t get_crc32table_le(void);
++
++/**
++ * intel_qrk_esram_test_contig_perfmetric
++ *
++ * Do a CRC16 for a contigous area of memory
++ * Map contigous area and get a CRC16
++ *
++ * Ensure overlayed data takes less time than regular unoverlayed DRAM
++ */
++int intel_qrk_esram_test_contig_perfmetric(void)
++{
++ u32 crcsize = 0x60000;
++ unsigned long long crc32_fullmap = 0, crc32_fullunmap = 0;
++ uint32_t crc32table_le = kallsyms_lookup_name("crc32table_le");
++ int ret = 0;
++
++ if (crc32table_le == 0){
++ pr_err("%s unable to fine symbol crc32table_le\n", __func__);
++ return -ENODEV;
++ }
++
++ /* Get raw data metric */
++ crc_cache = 1;
++ crc32_fullunmap = intel_qrk_crctest(esram_test_dev.pdata, crcsize);
++
++ /* Map CRC16 symbol (algorithm) + code (data) */
++ ret = intel_qrk_esram_map_symbol(crc32_le);
++ if(ret){
++ bist_err(ret);
++ }
++ ret = intel_qrk_esram_map_symbol((void*)crc32table_le);
++ if(ret){
++ bist_err(ret);
++ }
++
++ /* Map test data */
++ ret = intel_qrk_esram_map_range(esram_test_dev.pdata, crcsize, name);
++ if(ret){
++ bist_err(ret);
++ }
++
++ /* Get metric */
++ crc_cache = 1;
++ crc32_fullmap = intel_qrk_crctest(esram_test_dev.pdata, crcsize);
++#if 0
++ /* Tidy up */
++ ret = intel_qrk_esram_unmap_range(esram_test_dev.pdata, crcsize, name);
++ if(ret){
++ bist_err(ret);
++ }
++ ret = intel_qrk_esram_unmap_range(((void*)crc32_table),
++ sizeof(crc32_table), name);
++ if(ret){
++ bist_err(ret);
++ }
++ ret = intel_qrk_esram_unmap_symbol(crc32);
++ if(ret){
++ bist_err(ret);
++ }
++#endif
++ pr_info("%s did crctest - mapped - in %llu ticks\n", __func__, crc32_fullmap);
++ pr_info("%s mapped count %llu unmapped %llu\n",
++ __func__, crc32_fullmap, crc32_fullunmap);
++ return crc32_fullmap < crc32_fullunmap;
++}
++
++/**
++ * intel_qrk_esram_test_kernel_codemap
++ *
++ * Maps some kernel code - a data section and then calls the code contained
++ * therein. Proves out the running overlayed eSRAM works
++ */
++int intel_qrk_esram_test_kernel_codemap(void)
++{
++#if 0
++ int ret = intel_qrk_esram_map_symbol(msleep);
++ if(ret){
++ printk(KERN_ERR "%s map symbol msleep fail\n", __FUNCTION__);
++ bist_err(ret);
++ }
++
++ /* run the mapped code */
++ msleep(1);
++
++ /* unmap */
++ ret = intel_qrk_esram_unmap_symbol(msleep);
++ if(ret){
++ printk(KERN_ERR "%s unmap symbol msleep fail\n", __FUNCTION__);
++ bist_err(ret);
++ }
++#endif
++ return 0;
++}
++
++/**
++ * intel_qrk_esram_test_kernel_datamap
++ *
++ * Tests mapping/unmapping of a kernel data structure
++ */
++int intel_qrk_esram_test_kernel_datamap(void)
++{
++#if 0
++ unsigned long jtag = 0;
++ unsigned long ctrl = 0;
++
++ /* Map the interrupt descriptor table */
++ int ret = intel_qrk_esram_map_range(idt_table, INTEL_QRK_ESRAM_PAGE_SIZE, name);
++ if(ret){
++ bist_err(ret);
++ }
++
++ jtag = jiffies;
++ /* Wait for jiffies to tick or timeout to occur (failure) */
++ while(jtag == jiffies){
++ ctrl++;
++ }
++
++ /* unmap */
++ ret = intel_qrk_esram_unmap_range(idt_table, INTEL_QRK_ESRAM_PAGE_SIZE, name);
++ if(ret){
++ bist_err(ret);
++ }
++#endif
++ return 0;
++}
++
++/**
++ * intel_qrk_esram_test_sub_unsub
++ *
++ * Subscribe and unsubscribe 100% of available eSRAM
++ */
++int intel_qrk_esram_test_sub_unsub(void)
++{
++ int ret = 0;
++ u32 idx = 0, size = INTEL_QRK_ESRAM_PAGE_SIZE * INTEL_QRK_ESRAM_PAGE_COUNT;
++
++ /* Set a known state */
++ for(idx = 0; idx < size; idx += sizeof(u32)){
++ *((u32*)&esram_test_dev.pdata[idx]) = idx;
++ }
++
++ /* Basic test of full range of memory */
++ ret = intel_qrk_esram_map_range(esram_test_dev.pdata, size, name);
++ if(ret){
++ bist_err(ret);
++ }
++ for(idx = 0; idx < size; idx += sizeof(u32)){
++ if(*((u32*)&esram_test_dev.pdata[idx]) != idx){
++ pr_err("Entry %d is 0x%08x require 0x%08x",
++ idx, esram_test_dev.pdata[idx], idx);
++ bist_err(-EIO);
++ }
++ }
++#if 0
++ ret = intel_qrk_esram_unmap_range(esram_test_dev.pdata, size, name);
++ if(ret){
++ bist_err(ret);
++ }
++#endif
++ return 0;
++}
++
++/**
++ * intel_qrk_esram_test_over_sub
++ *
++ * Test oversubscription of eSRAM
++ */
++int intel_qrk_esram_test_over_sub(void)
++{
++ int ret = 0;
++ u32 size = INTEL_QRK_ESRAM_PAGE_SIZE * (INTEL_QRK_ESRAM_PAGE_COUNT + 1);
++
++ /* Over subscribe should fail */
++ ret = intel_qrk_esram_map_range(esram_test_dev.pdata, size, name);
++ if(ret == 0){
++ //intel_qrk_esram_unmap_range(esram_test_dev.pdata, size, name);
++ bist_err(-EFAULT);
++ }
++ return 0;
++}
++
++/*
++ * File ops
++ */
++static long esram_test_ioctl(struct file *file, unsigned int cmd,
++ unsigned long arg)
++{
++ int ret = -EINVAL;
++
++ cmd -= QRK_ESRAM_IOCTL_BASE;
++ switch (cmd) {
++ case QRK_F_SW_APP_ESRAM_0:
++ /* Per page overlay */
++ ret = intel_qrk_esram_test_perpage_overlay();
++ break;
++
++ case QRK_F_SW_APP_ESRAM_1:
++ /* Verify page reference counting */
++ ret = intel_qrk_esram_test_pagref_count();
++ break;
++
++ case QRK_F_SW_APP_ESRAM_2:
++ /* Performance metric or overlay contig RAM */
++ ret = intel_qrk_esram_test_contig_perfmetric();
++ if (ret == 1)
++ ret = 0;
++ break;
++
++ case QRK_F_SW_APP_ESRAM_3:
++ /* Verify mapping of kernel code section */
++ /* Covered by test #2 */
++ ret = 0; //intel_qrk_esram_test_kernel_codemap();
++ break;
++
++ case QRK_F_SW_APP_ESRAM_4:
++ /* Verify mapping of kernel data section (IDT) */
++ /* Covered by test #2 */
++ ret = 0; //intel_qrk_esram_test_kernel_datamap();
++ break;
++
++ case QRK_F_SW_APP_ESRAM_5:
++ /* Complete subscribe/unsubscribe eSRAM */
++ ret = intel_qrk_esram_test_sub_unsub();
++ break;
++
++ case QRK_F_SW_APP_ESRAM_6:
++ /* Over subscribe eSRAM */
++ ret = intel_qrk_esram_test_over_sub();
++ break;
++
++ default:
++ break;
++ }
++
++ return ret;
++}
++
++static int esram_test_open(struct inode *inode, struct file *file)
++{
++ mutex_lock(&esram_test_mutex);
++ nonseekable_open(inode, file);
++
++ if (mutex_lock_interruptible(&esram_test_dev.open_lock)) {
++ mutex_unlock(&esram_test_mutex);
++ return -ERESTARTSYS;
++ }
++
++ if (esram_test_dev.opened) {
++ mutex_unlock(&esram_test_dev.open_lock);
++ mutex_unlock(&esram_test_mutex);
++ return -EINVAL;
++ }
++
++ esram_test_dev.opened++;
++ mutex_unlock(&esram_test_dev.open_lock);
++ mutex_unlock(&esram_test_mutex);
++
++ return 0;
++}
++
++static int esram_test_release(struct inode *inode, struct file *file)
++{
++ mutex_lock(&esram_test_dev.open_lock);
++ esram_test_dev.opened = 0;
++ mutex_unlock(&esram_test_dev.open_lock);
++
++ return 0;
++}
++
++static const struct file_operations esram_test_file_ops = {
++ .open = esram_test_open,
++ .release = esram_test_release,
++ .unlocked_ioctl = esram_test_ioctl,
++ .llseek = no_llseek,
++};
++
++
++/**
++ * intel_qrk_esram_test_probe
++ *
++ * @param pdev: Platform device
++ * @return 0 success < 0 failure
++ *
++ * Callback from platform sub-system to probe
++ *
++ * This driver manages eSRAM on a per-page basis. Therefore if we find block
++ * mode is enabled, or any global, block-level or page-level locks are in place
++ * at module initialisation time - we bail out.
++ */
++static int intel_qrk_esram_test_probe(struct platform_device * pdev)
++{
++ int retval = 0;
++ unsigned int minor = 0;
++
++ esram_test_dev.size = INTEL_QRK_ESRAM_PAGE_COUNT * INTEL_QRK_ESRAM_PAGE_SIZE;
++
++ /* Get memory */
++ esram_test_dev.pdata = kzalloc(esram_test_dev.size, GFP_KERNEL);
++ if(unlikely(esram_test_dev.pdata == NULL)){
++ pr_err("Can't allocate %d bytes\n", esram_test_dev.size);
++ return -ENOMEM;
++ }
++
++ mutex_init(&esram_test_dev.open_lock);
++ cdev_init(&esram_test_dev.cdev, &esram_test_file_ops);
++ esram_test_dev.cdev.owner = THIS_MODULE;
++
++ retval = cdev_add(&esram_test_dev.cdev, MKDEV(esram_test_major, minor), 1);
++ if (retval) {
++ printk(KERN_ERR "chardev registration failed\n");
++ kfree(esram_test_dev.pdata);
++ return -EINVAL;
++ }
++ if (IS_ERR(device_create(esram_test_class, NULL,
++ MKDEV(esram_test_major, minor), NULL,
++ "esramtest%u", minor))){
++ dev_err(&pdev->dev, "can't create device\n");
++ kfree(esram_test_dev.pdata);
++ return -EINVAL;
++ }
++ printk(KERN_INFO "%s/%s/%s complete OK !!\n", __FUNCTION__, __DATE__,__TIME__);
++ return 0;
++
++}
++
++/**
++ * intel_qrk_esram_remove
++ *
++ * @return 0 success < 0 failure
++ *
++ * Removes a platform device
++ */
++static int intel_qrk_esram_test_remove(struct platform_device * pdev)
++{
++ unsigned int minor = MINOR(esram_test_dev.cdev.dev);
++
++ device_destroy(esram_test_class, MKDEV(esram_test_major, minor));
++ cdev_del(&esram_test_dev.cdev);
++ kfree(esram_test_dev.pdata);
++
++ return 0;
++}
++
++/*
++ * Platform structures useful for interface to PM subsystem
++ */
++static struct platform_driver intel_qrk_esram_test_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ },
++ .remove = intel_qrk_esram_test_remove,
++};
++
++/**
++ * intel_qrk_esram_init
++ *
++ * @return 0 success < 0 failure
++ *
++ * Module entry point
++ */
++static int __init intel_qrk_esram_test_init(void)
++{
++ int retval = 0;
++ dev_t dev;
++
++ esram_test_class = class_create(THIS_MODULE,"qrk_esram_test");
++ if (IS_ERR(esram_test_class)) {
++ retval = PTR_ERR(esram_test_class);
++ printk(KERN_ERR "esram_test: can't register earam_test class\n");
++ goto err;
++ }
++
++ retval = alloc_chrdev_region(&dev, 0, 1, "esram_test");
++ if (retval) {
++ printk(KERN_ERR "earam_test: can't register character device\n");
++ goto err_class;
++ }
++ esram_test_major = MAJOR(dev);
++
++ memset(&esram_test_dev, 0x00, sizeof(esram_test_dev));
++ esram_test_dev.pldev = platform_create_bundle(
++ &intel_qrk_esram_test_driver, intel_qrk_esram_test_probe, NULL, 0, NULL, 0);
++
++ if(IS_ERR(esram_test_dev.pldev)){
++ printk(KERN_ERR "platform_create_bundle fail!\n");
++ retval = PTR_ERR(esram_test_dev.pldev);
++ goto err_class;
++ }
++
++ return 0;
++
++err_class:
++ class_destroy(esram_test_class);
++err:
++ return retval;
++}
++
++/**
++ * intel_qrk_esram_exit
++ *
++ * Module exit
++ */
++static void __exit intel_qrk_esram_test_exit(void)
++{
++ platform_device_unregister(esram_test_dev.pldev);
++ platform_driver_unregister(&intel_qrk_esram_test_driver);
++}
++
++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>");
++MODULE_DESCRIPTION("Intel Quark eSRAM ITS driver");
++MODULE_LICENSE("Dual BSD/GPL");
++
++module_init(intel_qrk_esram_test_init);
++module_exit(intel_qrk_esram_test_exit);
+diff --git a/drivers/platform/x86/quark/intel_qrk_esram_test.h b/drivers/platform/x86/quark/intel_qrk_esram_test.h
+new file mode 100644
+index 0000000..35ad2d8
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_esram_test.h
+@@ -0,0 +1,43 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/**
++ * intel_qrk_esram_test.h
++ *
++ * Define integers for ioctl operation
++ *
++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>
++ */
++
++#ifndef __INTEL_QRK_ESRAM_TEST_H__
++#define __INTEL_QRK_ESRAM_TEST_H__
++
++#define QRK_ESRAM_IOCTL_BASE 255
++#define QRK_F_SW_APP_ESRAM_0 0x00000000
++#define QRK_F_SW_APP_ESRAM_1 0x00000001
++#define QRK_F_SW_APP_ESRAM_2 0x00000002
++#define QRK_F_SW_APP_ESRAM_3 0x00000003
++#define QRK_F_SW_APP_ESRAM_4 0x00000004
++#define QRK_F_SW_APP_ESRAM_5 0x00000005
++#define QRK_F_SW_APP_ESRAM_6 0x00000006
++#define QRK_F_SW_APP_ESRAM_7 0x00000007
++#define QRK_F_SW_APP_ESRAM_8 0x00000008
++
++#endif /* __INTEL_QRK_ESRAM_TEST_H__ */
++
+diff --git a/drivers/platform/x86/quark/intel_qrk_imr.c b/drivers/platform/x86/quark/intel_qrk_imr.c
+new file mode 100644
+index 0000000..8865307
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_imr.c
+@@ -0,0 +1,697 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark IMR driver
++ *
++ * IMR stand for Insolate Memory Region, supported by Quark SoC.
++ *
++ * A total number of 8 IMRs have implemented by Quark SoC,
++ * Some IMRs might be already occupied by BIOS or Linux during
++ * booting time.
++ *
++ * A user can cat /sys/devices/platform/intel-qrk-imr/status for current IMR
++ * status
++ *
++ * To allocate an IMR addresses must be alinged to 1k
++ *
++ * The IMR alloc API will locate the next available IMR slot set up
++ * with input memory region, then apply the input access right masks
++ *
++ * The IMR can be freed with the pre-allocated memory addresses.
++ */
++
++#include <asm-generic/uaccess.h>
++#include <linux/intel_qrk_sb.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/proc_fs.h>
++
++#include "intel_qrk_imr.h"
++#include <asm/imr.h>
++
++#define DRIVER_NAME "intel-qrk-imr"
++
++#define IMR_READ_MASK 0x1
++#define IMR_MAX_ID 7
++
++#ifndef phys_to_virt
++#define phys_to_virt __va
++#endif
++
++/* IMR HW register address structre */
++struct qrk_imr_reg_t {
++ u8 imr_xl; /* high address register */
++ u8 imr_xh; /* low address register */
++ u8 imr_rm; /* read mask register */
++ u8 imr_wm; /* write mask register */
++} qrk_imr_reg_t;
++
++/**
++ * struct qrk_imr_addr_t
++ *
++ * IMR memory address structure
++ */
++struct qrk_imr_addr_t {
++ u32 addr_low; /* low boundary memroy address */
++ u32 addr_high; /* high boundary memory address */
++ u32 read_mask; /* read access right mask */
++ u32 write_mask; /* write access right mask */
++} qrk_imr_addr_t;
++
++/**
++ * struct qrk_imr_pack
++ *
++ * local IMR pack structure
++ */
++struct qrk_imr_pack {
++ bool occupied; /* IMR occupied */
++ bool locked; /* IMR lock */
++ struct qrk_imr_reg_t reg; /* predefined imr register address */
++ struct qrk_imr_addr_t addr; /* IMR address region structure */
++ unsigned char info[MAX_INFO_SIZE]; /* IMR info */
++} qrk_imr_pack;
++
++
++/* Predefined HW register address */
++static struct qrk_imr_reg_t imr_reg_value[] = {
++ { IMR0L, IMR0H, IMR0RM, IMR0WM },
++ { IMR1L, IMR1H, IMR1RM, IMR1WM },
++ { IMR2L, IMR2H, IMR2RM, IMR2WM },
++ { IMR3L, IMR3H, IMR3RM, IMR3WM },
++ { IMR4L, IMR4H, IMR4RM, IMR4WM },
++ { IMR5L, IMR5H, IMR5RM, IMR5WM },
++ { IMR6L, IMR6H, IMR6RM, IMR6WM },
++ { IMR7L, IMR7H, IMR7RM, IMR7WM }
++};
++
++static struct platform_device *pdev;
++
++/**
++ * module parameter
++ * IMR slot should repersant the available IMR region from
++ * linux boot and BIOS.
++ *
++ * For example: imr_bit_mask = 0x10111001
++ * occupied IMR: 0, 3, 4, 5, 7
++ * un-occupied IMR: 1, 2, 6
++ */
++static int imr_bit_mask = 0xFF;
++module_param(imr_bit_mask, int, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(imr_bit_mask, "IMR bit mask");
++
++/**
++ * module parameter
++ * if IMR lock is a nozero value, all unlocked
++ * imrs will be locked regardless the usage.
++ */
++static int imr_lock = 0;
++module_param(imr_lock, int, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(imr_lock, "switch to lock unused IMR");
++
++/* local IMR data structure */
++struct qrk_imr_pack local_imr[IMR_MAXID];
++
++static unsigned short host_id;
++
++/**
++ * intel_qrk_imr_read_reg
++ *
++ * @param reg: register address
++ * @return nothing
++ *
++ * return register value from input address.
++ */
++static inline uint32_t intel_qrk_imr_read_reg(uint8_t reg)
++{
++ uint32_t temp = 0;
++
++ intel_qrk_sb_read_reg(SB_ID_ESRAM, CFG_READ_OPCODE, reg, &temp, 0);
++ return temp;
++}
++
++/**
++ * intel_clm_imr_latch_data
++ *
++ * @return nothing
++ *
++ * Populate IMR data structure from HW.
++ */
++static inline void intel_clm_imr_latch_data(void)
++{
++ int i = 0;
++
++ for (i = 0; i < IMR_MAXID; i++) {
++
++ local_imr[i].addr.addr_low =
++ intel_qrk_imr_read_reg(imr_reg_value[i].imr_xl);
++ local_imr[i].addr.addr_high =
++ intel_qrk_imr_read_reg(imr_reg_value[i].imr_xh);
++ local_imr[i].addr.read_mask =
++ intel_qrk_imr_read_reg(imr_reg_value[i].imr_rm);
++ local_imr[i].addr.write_mask =
++ intel_qrk_imr_read_reg(imr_reg_value[i].imr_wm);
++
++ if (local_imr[i].addr.addr_low & IMR_LOCK_BIT)
++ local_imr[i].locked = true;
++
++ if (local_imr[i].addr.read_mask > 0 &&
++ local_imr[i].addr.read_mask < IMR_READ_ENABLE_ALL){
++ local_imr[i].occupied = true;
++ } else {
++ local_imr[i].occupied = false;
++ memcpy(local_imr[i].info, "NOT USED", MAX_INFO_SIZE);
++ }
++ }
++}
++
++/**
++ * prepare_input_addr
++ *
++ * @param addr: input physical memory address
++ * @return formated memory address
++ *
++ * 1. verify input memory address alignment
++ * 2. apply IMR_REG_MASK to match the format required by HW
++ */
++static inline uint32_t prepare_input_addr(uint32_t addr)
++{
++ if (addr & (IMR_MEM_ALIGN - 1))
++ return 0;
++
++ addr = (addr >> 8) & IMR_REG_MASK;
++ return addr;
++}
++
++/**
++ * intel_qrk_imr_find_free_entry
++ *
++ * @return the next free imr slot
++ */
++static int intel_qrk_imr_find_free_entry(void)
++{
++ int i = 0;
++
++ intel_clm_imr_latch_data();
++
++ for (i = 0; i < IMR_MAXID; i++) {
++ if ((!local_imr[i].occupied) && (!local_imr[i].locked))
++ return i;
++ }
++
++ pr_err("%s: No more free IMR available.\n", __func__);
++ return -ENOMEM;
++}
++
++
++/**
++ * sb_write_chk
++ *
++ * @param id: Sideband identifier
++ * @param cmd: Command to send to destination identifier
++ * @param reg: Target register w/r to qrk_sb_id
++ * @param data: Data to write to target register
++ * @return nothing
++ *
++ * Set SB register and read back to verify register has been updated.
++ */
++static void sb_write_chk(qrk_sb_id id, u8 cmd, u8 reg, u32 data)
++{
++ u32 data_verify = 0;
++
++ intel_qrk_sb_write_reg(id, cmd, reg, data, 0);
++ intel_qrk_sb_read_reg(id, cmd, reg, &data_verify, 0);
++ BUG_ON(data != data_verify);
++}
++
++/**
++ * imr_add_entry
++ *
++ * @param id: imr slot id
++ * @param hi: hi memory address
++ * @param lo: lo memory address
++ * @param read: read access mask
++ * @param write: write access mask
++ * @return nothing
++ *
++ */
++static inline void imr_add_entry(int id, uint32_t hi, uint32_t lo,
++ uint32_t read, uint32_t write, bool lock)
++{
++ u32 val = 0;
++
++ intel_qrk_sb_read_reg(SB_ID_ESRAM, CFG_READ_OPCODE,
++ imr_reg_value[id].imr_xl, &val, 0);
++ val &= ~IMR_EN;
++ sb_write_chk(SB_ID_ESRAM, CFG_WRITE_OPCODE, imr_reg_value[id].imr_xl,
++ val);
++
++ sb_write_chk(SB_ID_ESRAM, CFG_WRITE_OPCODE, imr_reg_value[id].imr_rm,
++ read);
++ sb_write_chk(SB_ID_ESRAM, CFG_WRITE_OPCODE, imr_reg_value[id].imr_wm,
++ write);
++ sb_write_chk(SB_ID_ESRAM, CFG_WRITE_OPCODE, imr_reg_value[id].imr_xh,
++ hi);
++ sb_write_chk(SB_ID_ESRAM, CFG_WRITE_OPCODE, imr_reg_value[id].imr_xl,
++ lo);
++}
++
++/**
++ * x1000_imr_add_entry
++ *
++ * @param id: imr slot id
++ * @param hi: hi memory address
++ * @param lo: lo memory address
++ * @param read: read access mask
++ * @param write: write access mask
++ * @return nothing
++ *
++ */
++static inline void x1000_imr_add_entry(int id, uint32_t hi, uint32_t lo,
++ uint32_t read, uint32_t write, bool lock)
++{
++ intel_qrk_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE,
++ imr_reg_value[id].imr_xl, lo, 0);
++ intel_qrk_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE,
++ imr_reg_value[id].imr_xh, hi, 0);
++ intel_qrk_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE,
++ imr_reg_value[id].imr_rm, read, 0);
++ intel_qrk_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE,
++ imr_reg_value[id].imr_wm, write, 0);
++}
++
++/**
++ * intel_qrk_imr_add_entry
++ *
++ * @param id: imr slot id
++ * @param hi: hi memory address
++ * @param lo: lo memory address
++ * @param read: read access mask
++ * @param write: write access mask
++ * @return nothing
++ *
++ * Setup an IMR entry
++ */
++static void intel_qrk_imr_add_entry(int id, uint32_t hi,
++ uint32_t lo, uint32_t read, uint32_t write, bool lock)
++{
++ if (PCI_DEVICE_ID_X1000_HOST_BRIDGE == host_id)
++ x1000_imr_add_entry(id, hi, lo, read, write, lock);
++ else
++ imr_add_entry(id, hi, lo, read, write, lock);
++
++ if (lock) {
++ lo |= IMR_LOCK_BIT;
++ intel_qrk_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE,
++ imr_reg_value[id].imr_xl, lo, 0);
++ }
++}
++
++/**
++ * get_phy_addr
++ * @return phy address value
++ *
++ * convert register format to physical address format.
++ */
++static uint32_t get_phy_addr(uint32_t reg_value)
++{
++ reg_value = ((reg_value & IMR_REG_MASK) << 8);
++ return reg_value;
++}
++
++
++
++/**
++ * intel_qrk_imr_init_mask
++ *
++ * @param mask: module parameter
++ * @return nothing
++ *
++ * prepare local IMR data structure from input module parameter.
++ */
++static void intel_qrk_imr_init_mask(int mask)
++{
++ int i = 0;
++
++ BUG_ON((mask > 255 || mask < 0));
++
++ for (i = 0; i < IMR_MAXID; i++) {
++ local_imr[i].addr.addr_low =
++ intel_qrk_imr_read_reg(imr_reg_value[i].imr_xl);
++
++ /* mask bit 1 means imr occupied*/
++ if (((mask>>i) & IMR_READ_MASK) == 0) {
++ if (!(local_imr[i].addr.addr_low & IMR_LOCK_BIT))
++ intel_qrk_remove_imr_entry(i);
++ }
++ }
++}
++
++/**
++ * imr_rm_entry
++ *
++ * @param id: imr slot id
++ * @return nothing
++ *
++ */
++static inline void imr_rm_entry(int id)
++{
++ u32 val = 0;
++
++ intel_qrk_sb_read_reg(SB_ID_ESRAM, CFG_READ_OPCODE,
++ imr_reg_value[id].imr_xl, &val, 0);
++ val &= ~IMR_EN;
++ sb_write_chk(SB_ID_ESRAM, CFG_WRITE_OPCODE, imr_reg_value[id].imr_xl,
++ val);
++}
++
++/**
++ * x1000_imr_rm_entry
++ *
++ * @param id: imr slot id
++ * @return nothing
++ *
++ */
++static inline void x1000_imr_rm_entry(int id)
++{
++ intel_qrk_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE,
++ imr_reg_value[id].imr_rm, IMR_READ_ENABLE_ALL,
++ 0);
++ intel_qrk_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE,
++ imr_reg_value[id].imr_wm, IMR_WRITE_ENABLE_ALL,
++ 0);
++ intel_qrk_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE,
++ imr_reg_value[id].imr_xl, IMR_BASE_ADDR, 0);
++ intel_qrk_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE,
++ imr_reg_value[id].imr_xh, IMR_BASE_ADDR, 0);
++}
++
++/**
++ * intel_qrk_remove_imr_entry
++ *
++ * @param id: imr slot id
++ * @return nothing
++ *
++ * remove imr slot based on input id
++ */
++void intel_qrk_remove_imr_entry(int id)
++{
++ if (id >= IMR_MAXID || local_imr[id].locked)
++ return;
++
++ if (PCI_DEVICE_ID_X1000_HOST_BRIDGE == host_id)
++ x1000_imr_rm_entry(id);
++ else
++ imr_rm_entry(id);
++
++ intel_clm_imr_latch_data();
++
++}
++EXPORT_SYMBOL(intel_qrk_remove_imr_entry);
++
++/**
++ * intel_qrk_imr_alloc
++ *
++ * @param high: high boundary of memory address
++ * @param low: low boundary of memorry address
++ * @param read: IMR read mask value
++ * @param write: IMR write mask value
++ * @return nothing
++ *
++ * setup the next available IMR with customized read and write masks
++ */
++int intel_qrk_imr_alloc(uint32_t high, uint32_t low, uint32_t read,
++ uint32_t write, unsigned char *info, bool lock)
++{
++ int id = 0;
++
++ if (info == NULL)
++ return -EINVAL;
++
++ if ((low & IMR_LOCK_BIT) || (read == 0 || write == 0)) {
++ pr_err("%s: Invalid acces mode\n", __func__);
++ return -EINVAL;
++ }
++
++ /* Calculate aligned addresses and validate range */
++ high = prepare_input_addr(high);
++ low = prepare_input_addr(low);
++
++ /* Find a free entry */
++ id = intel_qrk_imr_find_free_entry();
++ if (id < 0)
++ return -ENOMEM;
++
++ /* Add entry - locking as necessary */
++ intel_qrk_imr_add_entry(id, high, low, (read & IMR_READ_ENABLE_ALL),
++ write, lock);
++
++ /* Name the new entry */
++ memcpy(local_imr[id].info, info, MAX_INFO_SIZE);
++
++ /* Update local data structures */
++ intel_clm_imr_latch_data();
++
++ pr_info("IMR alloc id %d 0x%08x - 0x%08x %s\n", id, low, high,
++ lock ? "locked" : "unlocked");
++
++ return 0;
++}
++EXPORT_SYMBOL(intel_qrk_imr_alloc);
++
++/**
++ * intel_qrk_imr_free
++ *
++ * @param high: high boundary of memory address
++ * @param low: low boundary of memorry address
++ * @return nothing
++ *
++ * remove the imr based on input memory region
++ */
++int intel_qrk_imr_free(uint32_t high, uint32_t low)
++{
++ int i = 0;
++
++ if (low > high) {
++ pr_err("%s: Invalid input address values.\n", __func__);
++ return -EINVAL;
++ }
++
++ high = prepare_input_addr(high);
++ if (!high) {
++ pr_err("%s: Invalid input memory address.\n", __func__);
++ return -EINVAL;
++ }
++
++ low = prepare_input_addr(low);
++ if (!low) {
++ pr_err("%s: Invalid input memory address.\n", __func__);
++ return -EINVAL;
++ }
++
++ for (i = 0; i < IMR_MAXID; i++) {
++ if (local_imr[i].occupied
++ && (local_imr[i].addr.addr_low == low)
++ && (local_imr[i].addr.addr_high == high)
++ && (!local_imr[i].locked)) {
++ intel_qrk_remove_imr_entry(i);
++ return 0;
++ }
++ }
++
++ return -EINVAL;
++}
++EXPORT_SYMBOL(intel_qrk_imr_free);
++
++/**
++ * intel_qrk_imr_init_data
++ *
++ * @return nothing
++ * initialize local_imr data structure
++ */
++static void intel_qrk_imr_init_data(void)
++{
++ int i = 0;
++ char * res_str = "System Reserved Region";
++ int len = strlen(res_str);
++
++ intel_clm_imr_latch_data();
++
++ for (i = 0; i < IMR_MAXID; i++) {
++ local_imr[i].reg = imr_reg_value[i];
++ memcpy(local_imr[i].info, res_str, len);
++ }
++}
++
++/**
++ * intel_qrk_imr_lockall
++ *
++ * @param mask: module parameter
++ * @return nothing
++ *
++ * lock up all un-locked IMRs
++ */
++int intel_qrk_imr_lockall(void)
++{
++ int i = 0;
++ uint32_t temp_addr;
++
++ /* Enumerate IMR data structures */
++ intel_qrk_imr_init_data();
++ intel_qrk_imr_init_mask(imr_bit_mask);
++
++ /* Cycle through IMRs locking whichever are unlocked */
++ for (i = 0; i < IMR_MAXID; i++) {
++
++ temp_addr = local_imr[i].addr.addr_low;
++ if (!(temp_addr & IMR_LOCK_BIT)) {
++
++ DBG("%s: locking IMR %d\n", __func__, i);
++ temp_addr |= IMR_LOCK_BIT;
++ intel_qrk_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE,
++ local_imr[i].reg.imr_xl,
++ temp_addr, 0);
++ }
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL(intel_qrk_imr_lockall);
++
++/**
++ * intel_qrk_imr_stat_show
++ *
++ * @param dev: pointer to device
++ * @param attr: attribute pointer
++ * @param buf: output buffer
++ * @return number of bytes successfully read
++ *
++ * Populates IMR state via /sys/device/intel-qrk-imr/stat
++ */
++static int intel_qrk_imr_stat_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ int len = 0;
++ int i = 0;
++ int size, count = PAGE_SIZE;
++ uint32_t hi_phy_addr, lo_phy_addr;
++
++ for (i = 0; i < IMR_MAXID; i++) {
++
++ /* read back the actual input physical memory address */
++ hi_phy_addr = get_phy_addr(local_imr[i].addr.addr_high);
++ lo_phy_addr = get_phy_addr(local_imr[i].addr.addr_low);
++
++ /* the IMR always protect extra 1k memory size above the input
++ * high reg value
++ */
++ size = ((hi_phy_addr - lo_phy_addr) / IMR_MEM_ALIGN) + 1;
++
++ size = snprintf(buf+len, count,
++ "imr - id : %d\n"
++ "info : %s\n"
++ "occupied : %s\n"
++ "locked : %s\n"
++ "size : %d kb\n"
++ "hi addr (phy): 0x%08x\n"
++ "lo addr (phy): 0x%08x\n"
++ "hi addr (vir): 0x%08x\n"
++ "lo addr (vir): 0x%08x\n"
++ "read mask : 0x%08x\n"
++ "write mask : 0x%08x\n\n",
++ i,
++ local_imr[i].info,
++ local_imr[i].occupied ? "yes" : "no",
++ local_imr[i].locked ? "yes" : "no",
++ size,
++ hi_phy_addr,
++ lo_phy_addr,
++ (uint32_t)phys_to_virt(hi_phy_addr),
++ (uint32_t)phys_to_virt(lo_phy_addr),
++ local_imr[i].addr.read_mask,
++ local_imr[i].addr.write_mask);
++ len += size;
++ count -= size;
++ }
++ return len;
++}
++
++static struct device_attribute dev_attr_stats = {
++ .attr = {
++ .name = "stat",
++ .mode = 0444, },
++ .show = intel_qrk_imr_stat_show,
++};
++
++static struct attribute *platform_attributes[] = {
++ &dev_attr_stats.attr,
++ NULL,
++};
++
++static struct attribute_group imr_attrib_group = {
++ .attrs = platform_attributes
++};
++
++/**
++ * intel_qrk_imr_init
++ *
++ * @param dev_id: Host Bridge's PCI device ID
++ * @return 0 success < 0 failue
++ *
++ * module entry point
++ */
++int intel_qrk_imr_init(unsigned short dev_id)
++{
++ int ret;
++
++ host_id = dev_id;
++
++ pdev = platform_device_alloc(DRIVER_NAME, -1);
++ if (!pdev)
++ return -ENOMEM;
++
++ ret = platform_device_add(pdev);
++ if (ret)
++ goto fail_platform;
++
++ /* initialise local imr data structure */
++ intel_qrk_imr_init_data();
++
++ ret = sysfs_create_group(&pdev->dev.kobj, &imr_attrib_group);
++ if (ret)
++ goto fail_platform;
++
++ if(intel_qrk_imr_runt_setparams() == 0 && imr_lock == 1){
++ intel_qrk_imr_lockall();
++ }
++
++ return 0;
++
++fail_platform:
++ platform_device_del(pdev);
++ return ret;
++}
++EXPORT_SYMBOL(intel_qrk_imr_init);
++
++MODULE_DESCRIPTION("Intel Quark SOC IMR API ");
++MODULE_AUTHOR("Intel Corporation");
++MODULE_LICENSE("Dual BSD/GPL");
++
+diff --git a/drivers/platform/x86/quark/intel_qrk_imr.h b/drivers/platform/x86/quark/intel_qrk_imr.h
+new file mode 100644
+index 0000000..9f16c61
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_imr.h
+@@ -0,0 +1,157 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark IMR driver
++ *
++ * IMR stand for Insolate Memory Region, supported by Quark SoC.
++ *
++ * A total number of 8 IMRs have implemented by Quark SoC,
++ * some IMRs might be already occupied by BIOS or Linux booting time.
++ *
++ * Input addresses parameter required the actual Physical address.
++ *
++ * The IMR alloc API will locate the next available IMR slot set up
++ * with input memory region, and apply with the default access right
++ * (CPU & CPU_snoop enable).
++ *
++ * The alloc_mask API takes input read & write masks values to set up
++ * IMR with customized access right.
++ *
++ * User can free IMR with pre-alloc specified addresses.
++ */
++
++#ifndef __INTEL_QRK_IMR_H__
++#define __INTEL_QRK_IMR_H__
++
++#include <linux/intel_qrk_sb.h>
++#include "asm/io.h"
++
++#define CFG_READ_OPCODE 0x10 /* BUnit Control Read */
++#define CFG_WRITE_OPCODE 0x11 /* BUnit control write */
++
++/* DRAM IMR register addresses */
++#define IMR0L 0x40
++#define IMR0H 0x41
++#define IMR0RM 0x42
++#define IMR0WM 0x43
++#define IMR1L 0x44
++#define IMR1H 0x45
++#define IMR1RM 0x46
++#define IMR1WM 0x47
++#define IMR2L 0x48
++#define IMR2H 0x49
++#define IMR2RM 0x4A
++#define IMR2WM 0x4B
++#define IMR3L 0x4C
++#define IMR3H 0x4D
++#define IMR3RM 0x4E
++#define IMR3WM 0x4F
++#define IMR4L 0x50
++#define IMR4H 0x51
++#define IMR4RM 0x52
++#define IMR4WM 0x53
++#define IMR5L 0x54
++#define IMR5H 0x55
++#define IMR5RM 0x56
++#define IMR5WM 0x57
++#define IMR6L 0x58
++#define IMR6H 0x59
++#define IMR6RM 0x5A
++#define IMR6WM 0x5B
++#define IMR7L 0x5C
++#define IMR7H 0x5D
++#define IMR7RM 0x5E
++#define IMR7WM 0x5F
++
++#define IMR_EN 0x40000000
++#define IMR_LOCK_BIT 0x80000000
++#define IMR_WRITE_ENABLE_ALL 0xFFFFFFFF
++#define IMR_READ_ENABLE_ALL 0xBFFFFFFF
++#define IMR_REG_MASK 0xFFFFFC
++
++#define IMR_ESRAM_FLUSH_INIT 0x80000000 /* esram flush */
++#define IMR_SNOOP_ENABLE 0x40000000 /* core snoops */
++#define IMR_PUNIT_ENABLE 0x20000000
++#define IMR_SMM_ENABLE 0x02 /* core SMM access */
++#define IMR_NON_SMM_ENABLE 0x01 /* core non-SMM access */
++#define IMR_BASE_ADDR 0x00
++#define IMR_MEM_ALIGN 0x400
++
++#define MAX_INFO_SIZE 64
++#define IMR_MAXID 8
++
++/* snoop + Non SMM write mask */
++#define IMR_DEFAULT_MASK (IMR_SNOOP_ENABLE \
++ + IMR_ESRAM_FLUSH_INIT \
++ + IMR_NON_SMM_ENABLE)
++
++/* debug printk */
++#ifdef DEBUG
++#define DBG(args...) pr_info(args)
++#else
++#define DBG(args...)
++#endif
++
++extern unsigned long _text;
++extern unsigned long __init_begin;
++
++/**
++ * intel_qrk_imr_alloc
++ *
++ * @param high: the end of physical memory address
++ * @param low: the start of physical memory address
++ * @param read: IMR read mask value
++ * @param write: IMR write maks value
++ * @param info: imr information
++ * @param lock: imr lock
++ *
++ * Setup imr with customised read/ write masks
++ */
++int intel_qrk_imr_alloc(u32 high, u32 low, u32 read, u32 write,
++ unsigned char *info, bool lock);
++
++/**
++ * intel_qrk_imr_free
++ *
++ * @param high: high boundary of memory address
++ * @param low: low boundary of memorry address
++ *
++ * remove the imr based on input memory region
++ */
++int intel_qrk_imr_free(u32 high, u32 low);
++
++/**
++ * intel_qrk_remove_imr_entry
++ *
++ * @param id: internal imr data struct id
++ *
++ * Remove imr based on input imr data structure id
++ */
++void intel_qrk_remove_imr_entry(int id);
++
++/**
++ * intel_qrk_imr_init
++ *
++ * @param dev_id: Host Bridge's PCI device ID
++ * Initialise IMRs
++ */
++int intel_qrk_imr_init(unsigned short dev_id);
++
++#endif
+diff --git a/drivers/platform/x86/quark/intel_qrk_imr_kernel.c b/drivers/platform/x86/quark/intel_qrk_imr_kernel.c
+new file mode 100644
+index 0000000..fdfaea3
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_imr_kernel.c
+@@ -0,0 +1,139 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark IMR driver
++ *
++ * IMR stand for Insolate Memory Region, supported by Quark SoC.
++ *
++ * The IMR id 3 is pre-defined as the use for kernel data protection
++ *
++ * The early imr protects entire memory (from the beginning of kernel text
++ * section to the top of memory) during linux boot time. In the linux run
++ * time, the protection need to resize down to memory region that only
++ * contains: kernel text, read only data, and initialized data section.
++ *
++ */
++#include <linux/errno.h>
++#include <linux/intel_qrk_sb.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/platform_data/quark.h>
++#include <linux/printk.h>
++#include "intel_qrk_imr.h"
++
++/* pre-defined imr id for uncompressed kernel */
++#define IMR_KERNEL_ID 3
++
++/**
++ * addr_hw_ready
++ *
++ * shift input address value to match HW required 1k aligned format
++ */
++static inline uint32_t addr_hw_ready(uint32_t addr)
++{
++ /* memory alignment */
++ addr &= (~((1 << 10) - 1));
++
++ /* prepare input addr in HW required format */
++ addr = (addr >> 8) & IMR_REG_MASK;
++ return addr;
++}
++
++/**
++ * void intel_qrk_imr_runt_kerndata_setup
++ *
++ * Setup imr for kernel text, read only data section
++ *
++ * The read only data (rodata) section placed between text and initialized data
++ * section by kernel.
++ */
++static void intel_qrk_imr_runt_kerndata_setup(void)
++{
++ uint32_t hi;
++ uint32_t lo;
++
++ hi = (uint32_t)virt_to_phys(&__init_begin);
++ lo = (uint32_t)virt_to_phys(&_text);
++
++ /* Set a locked IMR around the kernel .text section */
++ if (intel_qrk_imr_alloc((hi - IMR_MEM_ALIGN), lo,
++ IMR_DEFAULT_MASK, IMR_DEFAULT_MASK,
++ "KERNEL RUNTIME DATA", 1)) {
++ pr_err("IMR: Set up runtime kernel data imr faild!\n");
++ return;
++ }
++}
++
++/**
++ * intel_qrk_imr_teardown_unlocked
++ *
++ * Remove any unlocked IMR
++ *
++ */
++static void intel_qrk_imr_teardown_unlocked(void)
++{
++ int i = 0;
++ for (i = 0; i < IMR_MAXID; i++)
++ intel_qrk_remove_imr_entry(i);
++}
++
++/**
++ * intel_qrk_imr_runt_setparams
++ *
++ * set imr range for text, read only, initialised data in linux run time
++ */
++int intel_qrk_imr_runt_setparams(void)
++{
++ /* Setup an IMR around the kernel .text area */
++ intel_qrk_imr_runt_kerndata_setup();
++
++ /* Remove any other unlocked IMR */
++ intel_qrk_imr_teardown_unlocked();
++
++ return 0;
++}
++EXPORT_SYMBOL(intel_qrk_imr_runt_setparams);
++
++/**
++ * intel_qrk_imr_runt_init
++ *
++ * module entry point
++ */
++static int __init intel_qrk_imr_runt_init(void)
++{
++ return 0;
++}
++
++/**
++ * intel_qrk_imr_runt_exit
++ *
++ * Module exit
++ */
++static void __exit intel_qrk_imr_runt_exit(void)
++{
++ /* do nothing */
++}
++
++MODULE_DESCRIPTION("Intel Quark SOC IMR API ");
++MODULE_AUTHOR("Intel Corporation");
++MODULE_LICENSE("Dual BSD/GPL");
++
++subsys_initcall(intel_qrk_imr_runt_init);
++module_exit(intel_qrk_imr_runt_exit);
+diff --git a/drivers/platform/x86/quark/intel_qrk_imr_test.c b/drivers/platform/x86/quark/intel_qrk_imr_test.c
+new file mode 100644
+index 0000000..4f2096a
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_imr_test.c
+@@ -0,0 +1,357 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark IMR Test module
++ *
++ */
++
++#include <linux/cdev.h>
++#include <linux/device.h>
++#include <linux/fs.h>
++#include <linux/intel_qrk_sb.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/slab.h>
++#include "intel_qrk_imr.h"
++
++#define DRIVER_NAME "intel_qrk_imr_test"
++
++/**
++ * XXX intel_qrk_sb.h needs to be updated with SB_ID_PUNIT and change
++ * propagated. This is a workaround to make it look less ugly. */
++#define SB_ID_PUNIT SB_ID_THERMAL
++
++/* Memory-mapped SPI Flash address */
++#define ILB_SPIFLASH_BASEADDR 0xFF800000
++/* PUnit DMA block transfer size, in bytes */
++#define SPI_DMA_BLOCK_SIZE 512
++
++/**************************** Exported to LISA *******************************/
++
++/*
++ * Internally-used ioctl code. At the moment it is not reserved by any mainline
++ * driver.
++ */
++#define IMR_TEST_IOCTL_CODE 0xE1
++
++/*
++ * Integers for ioctl operation.
++ */
++#define IOCTL_QRK_SANITY_CHECK_PUNIT_DMA _IO(IMR_TEST_IOCTL_CODE, 0x00)
++#define IOCTL_QRK_IMR_1 _IO(IMR_TEST_IOCTL_CODE, 0x01)
++
++/*****************************************************************************/
++
++/**
++ * struct intel_qrk_imr_dev
++ *
++ * Structure to represent module state/data/etc
++ */
++struct intel_qrk_imr_test_dev {
++ unsigned int opened;
++ struct platform_device *pldev; /* Platform device */
++ struct cdev cdev;
++ struct mutex open_lock;
++};
++
++static struct intel_qrk_imr_test_dev imr_test_dev;
++static struct class *imr_test_class;
++static DEFINE_MUTEX(imr_test_mutex);
++static int imr_test_major;
++
++/* PUnit DMA registers over side-band */
++#define PUNIT_SPI_DMA_COUNT_REG 0x60
++#define PUNIT_SPI_DMA_DEST_REG 0x61
++#define PUNIT_SPI_DMA_SRC_REG 0x62
++
++/**
++ * ilb_spi_dma_read
++ *
++ * @param src: physical address in Legacy SPI Flash
++ * @param dst: physical address of destination
++ * @param dma_block_count: number of 512B SPI Flash blocks to be transferred
++ *
++ * Read-access iLB SPI via PUnit DMA engine.
++ *
++ */
++static void ilb_spi_dma_read(u32 *src, u32 *dst, u32 dma_block_count)
++{
++ pr_info("%s: src=%p, dst=%p, count=%u\n", __func__, src, dst,
++ dma_block_count);
++
++ /* Setup source and destination addresses. */
++ intel_qrk_sb_write_reg(SB_ID_PUNIT, CFG_WRITE_OPCODE,
++ PUNIT_SPI_DMA_SRC_REG, (u32) src, 0);
++ intel_qrk_sb_write_reg(SB_ID_PUNIT, CFG_WRITE_OPCODE,
++ PUNIT_SPI_DMA_DEST_REG, (u32) dst, 0);
++
++ pr_info("%s: starting transaction\n", __func__);
++
++ /*
++ * Setup the number of block to be copied over. Transaction will start
++ * as soon as the register is filled with value.
++ */
++ intel_qrk_sb_write_reg(SB_ID_PUNIT, CFG_WRITE_OPCODE,
++ PUNIT_SPI_DMA_COUNT_REG, dma_block_count, 0);
++
++ /* Poll for completion. */
++ while (dma_block_count > 0) {
++ intel_qrk_sb_read_reg(SB_ID_PUNIT, CFG_READ_OPCODE,
++ PUNIT_SPI_DMA_COUNT_REG, &dma_block_count, 0);
++ }
++
++ pr_info("%s: transaction completed\n", __func__);
++}
++
++/**
++ * punit_dma_sanity_check
++ *
++ * @return 0 if success, 1 if failure
++ *
++ * Perform a basic sanity check for PUnit DMA engine. Copy over a 512B SPI
++ * Flash block.
++ */
++static int punit_dma_sanity_check(void)
++{
++ int err = 0;
++ u32 *buffer = NULL;
++ u32 buf_ph_addr = 0;
++
++ /* Allocate 512B buffer for 1 SPI Flash block */
++ buffer = kzalloc(SPI_DMA_BLOCK_SIZE, GFP_KERNEL);
++ if (!buffer) {
++ err = -ENOMEM;
++ goto end;
++ }
++
++ /* DMA first SPI Flash block into buffer */
++ buf_ph_addr = (u32)virt_to_phys(buffer);
++ ilb_spi_dma_read((u32 *)ILB_SPIFLASH_BASEADDR, (u32 *)buf_ph_addr, 1);
++
++ kfree(buffer);
++end:
++ return err;
++}
++
++/**
++ * imr_violate_kernel_punit_dma
++ *
++ * @return always 0
++ *
++ * PUnit-DMA access to the Uncompressed Kernel IMR.
++ * This is based on set_imr_kernel_data() in intel_qrk_imr.c. Find the physical
++ * address of .text section and copy a 512B chunk of legacy SPI via PuUnit DMA.
++ *
++ */
++static int imr_violate_kernel_punit_dma(void)
++{
++ extern unsigned long _text;
++ u32 kernel_text = (u32)virt_to_phys(&_text);
++
++ /* We expect this to trigger an IMR violation reset */
++ ilb_spi_dma_read((u32 *)ILB_SPIFLASH_BASEADDR, (u32 *)kernel_text, 1);
++
++ /*
++ * If we're still alive, we have a serious bug:
++ * - we didn't appropriately target the IMR?
++ * - if we have, weren't we prevented from accessing?
++ * - if we weren't prevented, it's unlikely we're alive with a dirty
++ * text section
++ */
++ pr_err("%s: BUG: still running after DMAing into kernel text!?\n",
++ __func__);
++
++ return 0;
++}
++
++/*
++ * File ops
++ */
++static long imr_test_ioctl(struct file *file, unsigned int cmd,
++ unsigned long arg)
++{
++ int ret = -EINVAL;
++
++ switch (cmd) {
++ case IOCTL_QRK_SANITY_CHECK_PUNIT_DMA:
++ /* Check PUnit DMA actually works */
++ ret = punit_dma_sanity_check();
++ break;
++ case IOCTL_QRK_IMR_1:
++ /* Kernel IMR violation: PUnit DMA access */
++ ret = imr_violate_kernel_punit_dma();
++ break;
++ default:
++ break;
++ }
++
++ return ret;
++}
++
++static int imr_test_open(struct inode *inode, struct file *file)
++{
++ mutex_lock(&imr_test_mutex);
++ nonseekable_open(inode, file);
++
++ if (mutex_lock_interruptible(&imr_test_dev.open_lock)) {
++ mutex_unlock(&imr_test_mutex);
++ return -ERESTARTSYS;
++ }
++
++ if (imr_test_dev.opened) {
++ mutex_unlock(&imr_test_dev.open_lock);
++ mutex_unlock(&imr_test_mutex);
++ return -EINVAL;
++ }
++
++ imr_test_dev.opened++;
++ mutex_unlock(&imr_test_dev.open_lock);
++ mutex_unlock(&imr_test_mutex);
++ return 0;
++}
++
++static int imr_test_release(struct inode *inode, struct file *file)
++{
++ mutex_lock(&imr_test_dev.open_lock);
++ imr_test_dev.opened = 0;
++ mutex_unlock(&imr_test_dev.open_lock);
++
++ return 0;
++}
++
++static const struct file_operations imr_test_file_ops = {
++ .open = imr_test_open,
++ .release = imr_test_release,
++ .unlocked_ioctl = imr_test_ioctl,
++ .llseek = no_llseek,
++};
++
++/**
++ * intel_qrk_imr_test_probe
++ *
++ * @param pdev: Platform device
++ * @return 0 success < 0 failure
++ *
++ * Callback from platform sub-system to probe
++ */
++static int intel_qrk_imr_test_probe(struct platform_device * pdev)
++{
++ int retval = 0;
++ unsigned int minor = 0;
++
++ mutex_init(&imr_test_dev.open_lock);
++ cdev_init(&imr_test_dev.cdev, &imr_test_file_ops);
++ imr_test_dev.cdev.owner = THIS_MODULE;
++
++ retval = cdev_add(&imr_test_dev.cdev, MKDEV(imr_test_major, minor), 1);
++ if (retval) {
++ printk(KERN_ERR "chardev registration failed\n");
++ return -EINVAL;
++ }
++ if (IS_ERR(device_create(imr_test_class, NULL,
++ MKDEV(imr_test_major, minor), NULL,
++ "imrtest%u", minor))){
++ dev_err(&pdev->dev, "can't create device\n");
++ return -EINVAL;
++ }
++
++ return 0;
++
++}
++
++static int intel_qrk_imr_test_remove(struct platform_device * pdev)
++{
++ unsigned int minor = MINOR(imr_test_dev.cdev.dev);
++
++ device_destroy(imr_test_class, MKDEV(imr_test_major, minor));
++ cdev_del(&imr_test_dev.cdev);
++
++ class_destroy(imr_test_class);
++
++ return 0;
++}
++
++/*
++ * Platform structures useful for interface to PM subsystem
++ */
++static struct platform_driver intel_qrk_imr_test_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ },
++ .remove = intel_qrk_imr_test_remove,
++};
++
++/**
++ * intel_qrk_imr_test_init
++ *
++ * Load module.
++ */
++static int __init intel_qrk_imr_test_init(void)
++{
++ int retval = 0;
++ dev_t dev;
++
++ imr_test_class = class_create(THIS_MODULE,"qrk_imr_test");
++ if (IS_ERR(imr_test_class)) {
++ retval = PTR_ERR(imr_test_class);
++ printk(KERN_ERR "imr_test: can't register imr_test class\n");
++ goto err;
++ }
++
++ retval = alloc_chrdev_region(&dev, 0, 1, "imr_test");
++ if (retval) {
++ printk(KERN_ERR "earam_test: can't register character device\n");
++ goto err_class;
++ }
++ imr_test_major = MAJOR(dev);
++
++ memset(&imr_test_dev, 0x00, sizeof(imr_test_dev));
++ imr_test_dev.pldev = platform_create_bundle(
++ &intel_qrk_imr_test_driver, intel_qrk_imr_test_probe, NULL, 0, NULL, 0);
++
++ if(IS_ERR(imr_test_dev.pldev)){
++ printk(KERN_ERR "platform_create_bundle fail!\n");
++ retval = PTR_ERR(imr_test_dev.pldev);
++ goto err_class;
++ }
++
++ return 0;
++
++err_class:
++ class_destroy(imr_test_class);
++err:
++ return retval;
++}
++
++static void __exit intel_qrk_imr_test_exit(void)
++{
++ platform_device_unregister(imr_test_dev.pldev);
++ platform_driver_unregister(&intel_qrk_imr_test_driver);
++}
++
++module_init(intel_qrk_imr_test_init);
++module_exit(intel_qrk_imr_test_exit);
++
++MODULE_AUTHOR("Josef Ahmad <josef.ahmad@intel.com>");
++MODULE_DESCRIPTION("Quark IMR test module");
++MODULE_LICENSE("Dual BSD/GPL");
++
+diff --git a/drivers/platform/x86/quark/intel_qrk_plat_clanton_hill.c b/drivers/platform/x86/quark/intel_qrk_plat_clanton_hill.c
+new file mode 100644
+index 0000000..3c489e8
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_plat_clanton_hill.c
+@@ -0,0 +1,226 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark Legacy Platform Data Layout.conf accessor
++ *
++ * Simple Legacy SPI flash access layer
++ *
++ */
++
++#include <linux/errno.h>
++#include <linux/gpio.h>
++#include <linux/i2c.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/spi/pxa2xx_spi.h>
++#include <linux/spi/spi.h>
++
++#define DRIVER_NAME "ClantonHill"
++#define GPIO_RESTRICT_NAME "qrk-gpio-restrict-nc"
++
++/******************************************************************************
++ * Analog Devices AD7298 SPI Device Platform Data
++ ******************************************************************************/
++#include "linux/platform_data/ad7298.h"
++
++/* Maximum input voltage allowed for each ADC input, in milliVolts */
++#define AD7298_MAX_EXT_VIN 5000
++#define AD7298_MAX_EXT_VIN_EXT_BATT 30000
++#define AD7298_MAX_EXT_VIN_INT_BATT 9200
++
++static const struct ad7298_platform_data ad7298_platform_data = {
++ .ext_ref = false,
++ .ext_vin_max = { AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN_EXT_BATT, AD7298_MAX_EXT_VIN_INT_BATT }
++};
++
++/******************************************************************************
++ * Intel Quark SPI Controller Data
++ ******************************************************************************/
++static struct pxa2xx_spi_chip qrk_ffrd_spi_0_cs_0 = {
++ .gpio_cs = 8,
++};
++
++static struct spi_board_info spi_onboard_devs[] = {
++ {
++ .modalias = "ad7298",
++ .max_speed_hz = 5000000,
++ .platform_data = &ad7298_platform_data,
++ .mode = SPI_MODE_2,
++ .bus_num = 0,
++ .chip_select = 0,
++ .controller_data = &qrk_ffrd_spi_0_cs_0,
++ },
++};
++
++/******************************************************************************
++ * ST Microelectronics LIS331DLH I2C Device Platform Data
++ ******************************************************************************/
++#include <linux/platform_data/lis331dlh_intel_qrk.h>
++
++/* GPIO interrupt pins connected to the LIS331DLH */
++#define ST_ACCEL_INT1_GPIO 15
++#define ST_ACCEL_INT2_GPIO 4
++
++static struct lis331dlh_intel_qrk_platform_data lis331dlh_i2c_platform_data = {
++ .irq1_pin = ST_ACCEL_INT1_GPIO,
++};
++
++static struct gpio reserved_gpios[] = {
++ {
++ ST_ACCEL_INT1_GPIO,
++ GPIOF_IN,
++ "st_accel_i2c-int1"
++ },
++ {
++ ST_ACCEL_INT2_GPIO,
++ GPIOF_IN,
++ "st_accel_i2c-int2"
++ },
++};
++
++/* I2C device addresses */
++#define MAX9867_ADDR 0x18
++#define LIS331DLH_ADDR 0x19
++
++static struct i2c_adapter *i2c_adap;
++static struct i2c_board_info probed_i2c_lis331dlh = {
++ .platform_data = &lis331dlh_i2c_platform_data,
++};
++static struct i2c_board_info probed_i2c_max9867;
++static const unsigned short max9867_i2c_addr[] =
++ { MAX9867_ADDR, I2C_CLIENT_END };
++static const unsigned short lis331dlh_i2c_addr[] =
++ { LIS331DLH_ADDR, I2C_CLIENT_END };
++
++static int i2c_probe(struct i2c_adapter *adap, unsigned short addr)
++{
++ /* Always return success: the I2C clients are already known. */
++ return 1;
++}
++
++/**
++ * intel_qrk_spi_add_onboard_devs
++ *
++ * @return 0 on success or standard errnos on failure
++ *
++ * Registers onboard SPI device(s) present on the Clanton Hill platform
++ */
++static int intel_qrk_spi_add_onboard_devs(void)
++{
++ return spi_register_board_info(spi_onboard_devs,
++ ARRAY_SIZE(spi_onboard_devs));
++}
++
++/**
++ * intel_qrk_gpio_restrict_probe
++ *
++ * Make GPIOs pertaining to Firmware inaccessible by requesting them. The
++ * GPIOs are never released nor accessed by this driver.
++ */
++static int intel_qrk_gpio_restrict_probe(struct platform_device *pdev)
++{
++ int ret = 0;
++ static int gpio_done, spi_done;
++ struct i2c_client *max9867 = NULL, *lis331dlh = NULL;
++
++ if (gpio_done)
++ goto spi;
++ ret = gpio_request_array(reserved_gpios, ARRAY_SIZE(reserved_gpios));
++ if (ret)
++ goto end;
++ gpio_done = 1;
++
++spi:
++ if (spi_done)
++ goto i2c;
++ ret = intel_qrk_spi_add_onboard_devs();
++ if (ret)
++ goto end;
++ spi_done = 1;
++i2c:
++ i2c_adap = i2c_get_adapter(0);
++ if (NULL == i2c_adap) {
++ pr_info("%s: i2c adapter not ready yet. Deferring..\n",
++ __func__);
++ ret = -EPROBE_DEFER;
++ goto end;
++ }
++ strlcpy(probed_i2c_max9867.type, "intel-qrk-max9867", I2C_NAME_SIZE);
++ max9867 = i2c_new_probed_device(i2c_adap, &probed_i2c_max9867,
++ max9867_i2c_addr, i2c_probe);
++ strlcpy(probed_i2c_lis331dlh.type, "lis331dlh_qrk", I2C_NAME_SIZE);
++ lis331dlh = i2c_new_probed_device(i2c_adap, &probed_i2c_lis331dlh,
++ lis331dlh_i2c_addr, i2c_probe);
++ i2c_put_adapter(i2c_adap);
++
++ if (NULL == max9867 || NULL == lis331dlh) {
++ pr_err("%s: can't probe I2C devices\n", __func__);
++ ret = -ENODEV;
++ goto end;
++ }
++
++end:
++ return ret;
++}
++
++static struct platform_driver gpio_restrict_pdriver = {
++ .driver = {
++ .name = GPIO_RESTRICT_NAME,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_gpio_restrict_probe,
++};
++
++static int intel_qrk_plat_clanton_hill_probe(struct platform_device *pdev)
++{
++ int ret = 0;
++
++ ret = platform_driver_register(&gpio_restrict_pdriver);
++
++ return ret;
++}
++
++static int intel_qrk_plat_clanton_hill_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static struct platform_driver qrk_clanton_hill_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_plat_clanton_hill_probe,
++ .remove = intel_qrk_plat_clanton_hill_remove,
++};
++
++module_platform_driver(qrk_clanton_hill_driver);
++
++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>");
++MODULE_DESCRIPTION("Clanton Hill BSP Data");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_ALIAS("platform:"DRIVER_NAME);
++
+diff --git a/drivers/platform/x86/quark/intel_qrk_plat_clanton_peak.c b/drivers/platform/x86/quark/intel_qrk_plat_clanton_peak.c
+new file mode 100644
+index 0000000..9edcef7
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_plat_clanton_peak.c
+@@ -0,0 +1,227 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Clanton Peak board entry point
++ *
++ */
++
++#include <linux/errno.h>
++#include <linux/gpio.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/spi_bitbang.h>
++#include <linux/spi/spi_gpio.h>
++
++#define DRIVER_NAME "ClantonPeakSVP"
++#define GPIO_RESTRICT_NAME_NC "qrk-gpio-restrict-nc"
++#define GPIO_RESTRICT_NAME_SC "qrk-gpio-restrict-sc"
++
++
++/* GPIO connected to Test Equipment */
++#define SUT_GPIO_NC_3 0x03
++#define SUT_GPIO_NC_4 0x04
++#define SUT_GPIO_NC_5 0x05
++#define SUT_GPIO_NC_6 0x06
++#define SUT_GPIO_SC_2 0x0A
++#define SUT_GPIO_SC_3 0x0B
++#define SUT_GPIO_SC_4 0x0C
++#define SUT_GPIO_SC_5 0x0D
++
++#define GPIO_NC_BITBANG_SPI_BUS 2
++#define GPIO_SC_BITBANG_SPI_BUS 3
++
++static struct spi_board_info spi_onboard_devs[] = {
++ {
++ .modalias = "spidev",
++ .chip_select = 0,
++ .max_speed_hz = 50000000,
++ .bus_num = 0,
++ },
++ {
++ .modalias = "spidev",
++ .chip_select = 0,
++ .max_speed_hz = 50000000,
++ .bus_num = 1,
++ },
++};
++
++/*
++ * Define platform data for bitbanged SPI devices.
++ * Assign GPIO to SCK/MOSI/MISO
++ */
++static struct spi_gpio_platform_data spi_gpio_nc_data = {
++ .sck = SUT_GPIO_NC_3,
++ .mosi = SUT_GPIO_NC_4,
++ .miso = SUT_GPIO_NC_5,
++ .num_chipselect = 1,
++};
++static struct spi_gpio_platform_data spi_gpio_sc_data = {
++ .sck = SUT_GPIO_SC_2,
++ .mosi = SUT_GPIO_SC_3,
++ .miso = SUT_GPIO_SC_4,
++ .num_chipselect = 1,
++};
++
++/*
++ * Board information for bitbanged SPI devices.
++ */
++static const struct spi_board_info spi_gpio_nc_bi[] = {
++ {
++ .modalias = "spidev",
++ .max_speed_hz = 1000,
++ .bus_num = GPIO_NC_BITBANG_SPI_BUS,
++ .mode = SPI_MODE_0,
++ .platform_data = &spi_gpio_nc_data,
++ /* Assign GPIO to CS */
++ .controller_data = (void *)SUT_GPIO_NC_6,
++ },
++};
++static const struct spi_board_info spi_gpio_sc_bi[] = {
++ {
++ .modalias = "spidev",
++ .max_speed_hz = 1000,
++ .bus_num = GPIO_SC_BITBANG_SPI_BUS,
++ .mode = SPI_MODE_0,
++ .platform_data = &spi_gpio_sc_data,
++ /* Assign GPIO to CS */
++ .controller_data = (void *)SUT_GPIO_SC_5,
++ },
++};
++
++static struct platform_device spi_gpio_nc_pd = {
++ .name = "spi_gpio",
++ .id = GPIO_NC_BITBANG_SPI_BUS,
++ .dev = {
++ .platform_data = &spi_gpio_nc_data,
++ },
++};
++
++static struct platform_device spi_gpio_sc_pd = {
++ .name = "spi_gpio",
++ .id = GPIO_SC_BITBANG_SPI_BUS,
++ .dev = {
++ .platform_data = &spi_gpio_sc_data,
++ },
++};
++
++/**
++ * intel_qrk_spi_add_onboard_devs
++ *
++ * @return 0 on success or standard errnos on failure
++ *
++ * Registers onboard SPI device(s) present on the Clanton Peak platform
++ */
++static int intel_qrk_spi_add_onboard_devs(void)
++{
++ return spi_register_board_info(spi_onboard_devs,
++ ARRAY_SIZE(spi_onboard_devs));
++}
++
++static int register_bitbanged_spi(int nc)
++{
++ int ret = 0;
++
++ ret = platform_device_register(nc ? &spi_gpio_nc_pd : &spi_gpio_sc_pd);
++ if (ret)
++ goto err;
++
++ ret = spi_register_board_info(nc ? spi_gpio_nc_bi : spi_gpio_sc_bi,
++ nc ? ARRAY_SIZE(spi_gpio_nc_bi) :
++ ARRAY_SIZE(spi_gpio_sc_bi));
++ if (ret)
++ goto err_unregister;
++
++ return 0;
++
++err_unregister:
++ platform_device_unregister(nc ? &spi_gpio_nc_pd : &spi_gpio_sc_pd);
++err:
++ return ret;
++}
++
++static int intel_qrk_gpio_restrict_probe_nc(struct platform_device *pdev)
++{
++ return register_bitbanged_spi(1);
++}
++
++static int intel_qrk_gpio_restrict_probe_sc(struct platform_device *pdev)
++{
++ return register_bitbanged_spi(0);
++}
++
++static struct platform_driver gpio_restrict_pdriver_nc = {
++ .driver = {
++ .name = GPIO_RESTRICT_NAME_NC,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_gpio_restrict_probe_nc,
++};
++
++static struct platform_driver gpio_restrict_pdriver_sc = {
++ .driver = {
++ .name = GPIO_RESTRICT_NAME_SC,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_gpio_restrict_probe_sc,
++};
++
++static int intel_qrk_plat_clanton_peak_probe(struct platform_device *pdev)
++{
++ int ret = 0;
++
++ ret = platform_driver_register(&gpio_restrict_pdriver_nc);
++ if (ret) {
++ pr_err("%s: couldn't register %s platform driver\n",
++ __func__, gpio_restrict_pdriver_nc.driver.name);
++ }
++
++ ret = platform_driver_register(&gpio_restrict_pdriver_sc);
++ if (ret) {
++ pr_err("%s: couldn't register %s platform driver\n",
++ __func__, gpio_restrict_pdriver_sc.driver.name);
++ }
++
++ return intel_qrk_spi_add_onboard_devs();
++}
++
++static int intel_qrk_plat_clanton_peak_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static struct platform_driver clanton_peak_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_plat_clanton_peak_probe,
++ .remove = intel_qrk_plat_clanton_peak_remove,
++};
++
++module_platform_driver(clanton_peak_driver);
++
++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>");
++MODULE_DESCRIPTION("Clanton Peak BSP Data");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_ALIAS("platform:"DRIVER_NAME);
+diff --git a/drivers/platform/x86/quark/intel_qrk_plat_cross_hill.c b/drivers/platform/x86/quark/intel_qrk_plat_cross_hill.c
+new file mode 100644
+index 0000000..eb1960a
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_plat_cross_hill.c
+@@ -0,0 +1,392 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * CrossHill board entry point
++ */
++
++#include <linux/errno.h>
++#include <linux/gpio.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/spi/pxa2xx_spi.h>
++#include <linux/spi/spi.h>
++
++#define DRIVER_NAME "CrossHill"
++#define GPIO_RESTRICT_NAME_NC "qrk-gpio-restrict-nc"
++#define GPIO_RESTRICT_NAME_SC "qrk-gpio-restrict-sc"
++
++/*
++ * GPIO numbers to use for reading 4-bit Blackburn Peak SPI daughterboard ID
++ */
++#define SPI_BPEAK_RESET_GPIO 4
++#define SPI_BPEAK_ID0_GPIO 3
++#define SPI_BPEAK_ID1_GPIO 2
++#define SPI_BPEAK_ID2_GPIO 15
++#define SPI_BPEAK_ID3_GPIO 14
++
++static int nc_gpio_reg;
++static int sc_gpio_reg;
++
++static int cross_hill_probe;
++
++/*
++ * Blackburn Peak SPI daughterboard ID values
++ */
++enum {
++ QRK_SPI_BPEAK_ID_ZB_TI = 0xA,
++ QRK_SPI_BPEAK_ID_ZB_DIGI,
++ QRK_SPI_BPEAK_ID_ZB_INFR_NXP,
++ QRK_SPI_BPEAK_ID_ZB_EXEGIN_ATMEL,
++ QRK_SPI_BPEAK_ID_ADC_MAXIM,
++ QRK_SPI_BPEAK_ID_NONE
++};
++
++
++/******************************************************************************
++ * Analog Devices AD7298 SPI Device Platform Data
++ ******************************************************************************/
++#include "linux/platform_data/ad7298.h"
++
++/* Maximum input voltage allowed for each ADC input, in milliVolts */
++#define AD7298_MAX_EXT_VIN 5000
++
++static const struct ad7298_platform_data ad7298_platform_data = {
++ .ext_ref = false,
++ .ext_vin_max = { AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN }
++};
++
++/******************************************************************************
++ * Maxim 78M6610+LMU SPI Device Platform Data
++ ******************************************************************************/
++#include "linux/platform_data/max78m6610_lmu.h"
++
++static const struct max78m6610_lmu_platform_data max78m6610_lmu_pdata = {
++ .reset_gpio = SPI_BPEAK_RESET_GPIO,
++};
++
++/******************************************************************************
++ * Intel Quark SPI Controller Data
++ ******************************************************************************/
++static struct pxa2xx_spi_chip qrk_ffrd_spi_0_cs_0 = {
++ .gpio_cs = 8,
++};
++
++static struct pxa2xx_spi_chip qrk_ffrd_spi_1_cs_0 = {
++ .gpio_cs = 10,
++};
++
++static struct pxa2xx_spi_chip qrk_ffrd_spi_1_cs_1 = {
++ .gpio_cs = 11,
++};
++
++static struct spi_board_info spi_generic_devs[] = {
++ {
++ .modalias = "spidev",
++ .max_speed_hz = 50000000,
++ .platform_data = NULL,
++ .mode = SPI_MODE_0,
++ .bus_num = 1,
++ .chip_select = 0,
++ .controller_data = &qrk_ffrd_spi_1_cs_0,
++ },
++
++ {
++ .modalias = "spidev",
++ .max_speed_hz = 50000000,
++ .platform_data = NULL,
++ .mode = SPI_MODE_0,
++ .bus_num = 1,
++ .chip_select = 1,
++ .controller_data = &qrk_ffrd_spi_1_cs_1,
++ },
++
++};
++
++static struct spi_board_info spi_energy_adc_devs[] = {
++ {
++ .modalias = "max78m6610_lmu",
++ .max_speed_hz = 2000000,
++ .platform_data = &max78m6610_lmu_pdata,
++ .mode = SPI_MODE_3,
++ .bus_num = 1,
++ .chip_select = 0,
++ .controller_data = &qrk_ffrd_spi_1_cs_0,
++ },
++};
++
++
++
++/**
++ * intel_qrk_spi_add_onboard_devs
++ *
++ * @return 0 on success or standard errnos on failure
++ *
++ * Registers onboard SPI device(s) present on the Cross Hill platform
++ */
++static int intel_qrk_spi_add_onboard_devs(void)
++{
++ struct spi_board_info spi_onboard_devs[] = {
++ {
++ .modalias = "ad7298",
++ .max_speed_hz = 5000000,
++ .platform_data = &ad7298_platform_data,
++ .mode = SPI_MODE_2,
++ .bus_num = 0,
++ .chip_select = 0,
++ .controller_data = &qrk_ffrd_spi_0_cs_0,
++ },
++ };
++
++ return spi_register_board_info(spi_onboard_devs,
++ ARRAY_SIZE(spi_onboard_devs));
++}
++
++
++/**
++ * intel_qrk_spi_get_bpeak_id
++ *
++ * @param bpeak_id: The Blackburn Peak SPI ID obtained from the daughterboard
++ * @return 0 on success or standard errnos on failure
++ *
++ * Reads an ID from GPIO-connected pins on Blackburn peak SPI daughterboard
++ */
++static int intel_qrk_spi_get_bpeak_id(u8 *bpeak_id)
++{
++ int ret = 0;
++ struct gpio spi_bpeak_id_gpios[] = {
++ {
++ SPI_BPEAK_RESET_GPIO,
++ GPIOF_OUT_INIT_HIGH,
++ "spi_bpeak_reset"
++ },
++ {
++ SPI_BPEAK_ID0_GPIO,
++ GPIOF_IN,
++ "spi_bpeak_id0"
++ },
++ {
++ SPI_BPEAK_ID1_GPIO,
++ GPIOF_IN,
++ "spi_bpeak_id1"
++ },
++ {
++ SPI_BPEAK_ID2_GPIO,
++ GPIOF_IN,
++ "spi_bpeak_id2"
++ },
++ {
++ SPI_BPEAK_ID3_GPIO,
++ GPIOF_IN,
++ "spi_bpeak_id3"
++ }
++ };
++
++ /*
++ * Read a 4-bit ID value from ID GPIO inputs, which are only valid
++ * while a RESET GPIO output is asserted (active-low)
++ */
++ ret = gpio_request_array(spi_bpeak_id_gpios,
++ ARRAY_SIZE(spi_bpeak_id_gpios));
++ if (ret) {
++ pr_err("%s: Failed to allocate Blackburn Peak ID GPIO pins\n",
++ __func__);
++ return ret;
++ }
++
++ gpio_set_value(SPI_BPEAK_RESET_GPIO, 0);
++ *bpeak_id =
++ (gpio_get_value(SPI_BPEAK_ID3_GPIO) ? 1 << 3 : 0) |
++ (gpio_get_value(SPI_BPEAK_ID2_GPIO) ? 1 << 2 : 0) |
++ (gpio_get_value(SPI_BPEAK_ID1_GPIO) ? 1 << 1 : 0) |
++ (gpio_get_value(SPI_BPEAK_ID0_GPIO) ? 1 : 0);
++ gpio_set_value(SPI_BPEAK_RESET_GPIO, 1);
++
++ gpio_free_array(spi_bpeak_id_gpios,
++ ARRAY_SIZE(spi_bpeak_id_gpios));
++
++ return 0;
++}
++
++/**
++ * intel_qrk_spi_add_bpeak_devs
++ *
++ * @return 0 on success or standard errnos on failure
++ *
++ * Registers SPI device(s) indicated by the ID value obtained from a
++ * Blackburn Peak SPI daughterboard
++ */
++static int intel_qrk_spi_add_bpeak_devs(void)
++{
++ u8 spi_bpeak_id = 0;
++ int ret = 0;
++
++ ret = intel_qrk_spi_get_bpeak_id(&spi_bpeak_id);
++ if (ret) {
++ pr_err("%s: failed to obtain Blackburn Peak ID\n",
++ __func__);
++ return ret;
++ }
++
++ switch (spi_bpeak_id) {
++
++ case QRK_SPI_BPEAK_ID_NONE:
++ break;
++
++ case QRK_SPI_BPEAK_ID_ADC_MAXIM:
++ {
++ return spi_register_board_info(spi_energy_adc_devs,
++ ARRAY_SIZE(spi_energy_adc_devs));
++ }
++ case QRK_SPI_BPEAK_ID_ZB_EXEGIN_ATMEL:
++ {
++ pr_debug("QRK_SPI_BPEAK_ID_ZB_EXEGIN_ATMEL.\n");
++ return spi_register_board_info(spi_generic_devs,
++ ARRAY_SIZE(spi_generic_devs));
++ }
++ case QRK_SPI_BPEAK_ID_ZB_DIGI:
++ {
++ pr_debug("QRK_SPI_BPEAK_ID_ZB_DIGI load.\n");
++ return spi_register_board_info(spi_generic_devs,
++ ARRAY_SIZE(spi_generic_devs));
++
++ }
++ default:
++ pr_err("%s: Unsupported Blackburn Peak SPI ID %u\n",
++ __func__, spi_bpeak_id);
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++/** intel_qrk_spi_devs_addon
++ *
++ * addon spi device when gpio support in place
++ */
++static int intel_qrk_spi_devs_addon(void)
++{
++ int ret = 0;
++
++ if (cross_hill_probe != 1) {
++
++ ret = intel_qrk_spi_add_onboard_devs();
++ if (ret)
++ return ret;
++
++ ret = intel_qrk_spi_add_bpeak_devs();
++
++ cross_hill_probe = 1;
++ }
++
++ return ret;
++}
++
++/**
++ * intel_qrk_gpio_restrict_probe_nc
++ *
++ * Make GPIOs pertaining to Firmware inaccessible by requesting them. The
++ * GPIOs are never released nor accessed by this driver.
++ */
++static int intel_qrk_gpio_restrict_probe_nc(struct platform_device *pdev)
++{
++ int ret;
++ nc_gpio_reg = 1;
++
++ if (nc_gpio_reg == 1 && sc_gpio_reg == 1) {
++ ret = intel_qrk_spi_devs_addon();
++ if (ret)
++ return ret;
++ }
++ return 0;
++}
++
++/**
++ * intel_qrk_gpio_restrict_probe_sc
++ *
++ * Make GPIOs pertaining to Firmware inaccessible by requesting them. The
++ * GPIOs are never released nor accessed by this driver.
++ */
++static int intel_qrk_gpio_restrict_probe_sc(struct platform_device *pdev)
++{
++ int ret;
++ sc_gpio_reg = 1;
++
++ if (nc_gpio_reg == 1 && sc_gpio_reg == 1) {
++ ret = intel_qrk_spi_devs_addon();
++ if (ret)
++ return ret;
++ }
++ return 0;
++}
++
++static struct platform_driver gpio_restrict_pdriver_nc = {
++ .driver = {
++ .name = GPIO_RESTRICT_NAME_NC,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_gpio_restrict_probe_nc,
++};
++
++static struct platform_driver gpio_restrict_pdriver_sc = {
++ .driver = {
++ .name = GPIO_RESTRICT_NAME_SC,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_gpio_restrict_probe_sc,
++};
++
++static int intel_qrk_plat_cross_hill_probe(struct platform_device *pdev)
++{
++ int ret = 0;
++
++ ret = platform_driver_register(&gpio_restrict_pdriver_nc);
++ if (ret)
++ return ret;
++
++ return platform_driver_register(&gpio_restrict_pdriver_sc);
++}
++
++static int intel_qrk_plat_cross_hill_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static struct platform_driver qrk_cross_hill_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_plat_cross_hill_probe,
++ .remove = intel_qrk_plat_cross_hill_remove,
++};
++
++module_platform_driver(qrk_cross_hill_driver);
++
++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>");
++MODULE_DESCRIPTION("Cross Hill BSP Data");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_ALIAS("platform:"DRIVER_NAME);
++
+diff --git a/drivers/platform/x86/quark/intel_qrk_plat_galileo.c b/drivers/platform/x86/quark/intel_qrk_plat_galileo.c
+new file mode 100644
+index 0000000..0df0ac0
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_plat_galileo.c
+@@ -0,0 +1,398 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark Legacy Platform Data Layout.conf accessor
++ *
++ * Simple Legacy SPI flash access layer
++ *
++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013
++ */
++
++#include <linux/errno.h>
++#include <linux/gpio.h>
++#include <linux/i2c.h>
++#include <linux/i2c/at24.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/module.h>
++#include <linux/mfd/cy8c9540a.h>
++#include <linux/mfd/intel_qrk_gip_pdata.h>
++#include <linux/mtd/partitions.h>
++#include <linux/mtd/physmap.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/spi/pxa2xx_spi.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/flash.h>
++#include <linux/i2c/at24.h>
++
++#define DRIVER_NAME "Galileo"
++#define GPIO_RESTRICT_NAME "qrk-gpio-restrict-sc"
++#define LPC_SCH_SPINAME "spi-lpc-sch"
++
++/* GPIO line used to detect the LSB of the Cypress i2c address */
++#define GPIO_CYPRESS_A0 7
++/* GPIO line Cypress interrupts are routed to (in S0 power state) */
++#define GPIO_CYPRESS_INT_S0 13
++/* GPIO line Cypress interrupts are routed to (in S3 power state) */
++#define GPIO_CYPRESS_INT_S3 2
++
++/* Cypress i2c address depending on A0 value */
++#define CYPRESS_ADDR_A0_1 0x20
++#define CYPRESS_ADDR_A0_0 0x21
++#define EEPROM_ADDR_A0_1 0x50
++#define EEPROM_ADDR_A0_0 0x51
++
++/******************************************************************************
++ * Cypress I/O Expander Platform Data
++ ******************************************************************************/
++static struct cy8c9540a_pdata cy8c9540a_platform_data = {
++ .por_default = {
++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Output */
++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Int mask */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* PWM */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Inversion */
++ 0xe0, 0xe0, 0xff, 0xf3, 0x00, 0xff, 0xff, 0xff, /* Direction */
++ 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, /* P0 drive */
++ 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, /* P1 drive */
++ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* P2 drive */
++ 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, /* P3 drive */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* P4 drive */
++ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* P5 drive */
++ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* P6 drive */
++ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* P7 drive */
++ 0x00, 0xff, 0x00, /* PWM0 */
++ 0x00, 0xff, 0x00, /* PWM1 */
++ 0x00, 0xff, 0x00, /* PWM2 */
++ 0x00, 0xff, 0x00, /* PWM3 */
++ 0x00, 0xff, 0x00, /* PWM4 */
++ 0x00, 0xff, 0x00, /* PWM5 */
++ 0x00, 0xff, 0x00, /* PWM6 */
++ 0x00, 0xff, 0x00, /* PWM7 */
++ 0x00, 0xff, 0x00, /* PWM8 */
++ 0x00, 0xff, 0x00, /* PWM9 */
++ 0x00, 0xff, 0x00, /* PWM10 */
++ 0x00, 0xff, 0x00, /* PWM11 */
++ 0x00, 0xff, 0x00, /* PWM12 */
++ 0x00, 0xff, 0x00, /* PWM13 */
++ 0x00, 0xff, 0x00, /* PWM14 */
++ 0x00, 0xff, 0x00, /* PWM15 */
++ 0xff, /* PWM CLKdiv */
++ 0x02, /* EEPROM en */
++ 0x00 /* CRC */
++ },
++ .pwm2gpio_mapping = {
++ CY8C9540A_PWM_UNUSED,
++ 3,
++ CY8C9540A_PWM_UNUSED,
++ 2,
++ 9,
++ 1,
++ 8,
++ 0,
++ },
++ .gpio_base = 16,
++ .pwm_base = 0,
++ .irq_base = 64,
++};
++
++/* Cypress expander requires i2c master to operate @100kHz 'standard mode' */
++static struct intel_qrk_gip_pdata gip_pdata = {
++ .i2c_std_mode = 1,
++};
++static struct intel_qrk_gip_pdata *galileo_gip_get_pdata(void)
++{
++ return &gip_pdata;
++}
++
++/******************************************************************************
++ * Analog Devices AD7298 SPI Device Platform Data
++ ******************************************************************************/
++#include "linux/platform_data/ad7298.h"
++
++/* Maximum input voltage allowed for each ADC input, in milliVolts */
++#define AD7298_MAX_EXT_VIN 5000
++
++static const struct ad7298_platform_data ad7298_platform_data = {
++ .ext_ref = false,
++ .ext_vin_max = { AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN }
++};
++
++static struct at24_platform_data at24_platform_data = {
++ .byte_len = (11 * 1024),
++ .page_size = 1,
++ .flags = AT24_FLAG_ADDR16,
++};
++
++/******************************************************************************
++ * Intel Izmir i2c clients
++ ******************************************************************************/
++static struct i2c_board_info probed_i2c_cypress = {
++ .platform_data = &cy8c9540a_platform_data,
++};
++static struct i2c_board_info probed_i2c_eeprom = {
++ .platform_data = &at24_platform_data,
++};
++static struct i2c_adapter *i2c_adap;
++static const unsigned short cypress_i2c_addr[] =
++ { CYPRESS_ADDR_A0_1, CYPRESS_ADDR_A0_0, I2C_CLIENT_END };
++static const unsigned short eeprom_i2c_addr[] =
++ { EEPROM_ADDR_A0_1, EEPROM_ADDR_A0_0, I2C_CLIENT_END };
++
++/******************************************************************************
++ * Intel Quark SPI Controller Data
++ ******************************************************************************/
++static struct pxa2xx_spi_chip qrk_ffrd_spi_0_cs_0 = {
++ .gpio_cs = 8,
++};
++
++static struct pxa2xx_spi_chip qrk_ffrd_spi_1_cs_0 = {
++ .gpio_cs = 10,
++};
++
++#define LPC_SCH_SPI_BUS_ID 0x03
++
++/* TODO: extract this data from layout.conf encoded in flash */
++struct mtd_partition ilb_partitions [] = {
++ {
++ .name = "grub",
++ .size = 4096,
++ .offset = 0,
++ },
++ {
++ .name = "grub.conf",
++ .size = 0xA00,
++ .offset = 0x50500,
++ },
++ {
++ .name = "layout.conf",
++ .size = 4096,
++ .offset = 0x708000,
++ },
++ {
++ .name = "sketch",
++ .size = 0x40000,
++ .offset = 0x750000,
++ },
++ {
++ .name = "raw",
++ .size = 8192000,
++ .offset = 0,
++
++ },
++};
++
++static struct flash_platform_data ilb_flash = {
++ .type = "s25fl064k",
++ .parts = ilb_partitions,
++ .nr_parts = ARRAY_SIZE(ilb_partitions),
++};
++
++static struct spi_board_info spi_onboard_devs[] = {
++ {
++ .modalias = "m25p80",
++ .platform_data = &ilb_flash,
++ .bus_num = LPC_SCH_SPI_BUS_ID,
++ .chip_select = 0,
++ },
++ {
++ .modalias = "ad7298",
++ .max_speed_hz = 5000000,
++ .platform_data = &ad7298_platform_data,
++ .mode = SPI_MODE_2,
++ .bus_num = 0,
++ .chip_select = 0,
++ .controller_data = &qrk_ffrd_spi_0_cs_0,
++ },
++ {
++ .modalias = "spidev",
++ .chip_select = 0,
++ .controller_data = &qrk_ffrd_spi_1_cs_0,
++ .max_speed_hz = 50000000,
++ .bus_num = 1,
++ },
++};
++
++static struct gpio reserved_gpios[] = {
++ {
++ GPIO_CYPRESS_A0,
++ GPIOF_IN,
++ "cy8c9540a-a0",
++ },
++ {
++ GPIO_CYPRESS_INT_S0,
++ GPIOF_IN,
++ "cy8c9540a-int-s0",
++ },
++ {
++ GPIO_CYPRESS_INT_S3,
++ GPIOF_IN,
++ "cy8c9540a-int-s3",
++ },
++};
++
++static int eeprom_i2c_probe(struct i2c_adapter *adap, unsigned short addr)
++{
++ if (gpio_get_value(GPIO_CYPRESS_A0) && EEPROM_ADDR_A0_1 == addr)
++ return 1;
++ if (!gpio_get_value(GPIO_CYPRESS_A0) && EEPROM_ADDR_A0_0 == addr)
++ return 1;
++ return 0;
++}
++static int cypress_i2c_probe(struct i2c_adapter *adap, unsigned short addr)
++{
++ if (gpio_get_value(GPIO_CYPRESS_A0) && CYPRESS_ADDR_A0_1 == addr)
++ return 1;
++ if (!gpio_get_value(GPIO_CYPRESS_A0) && CYPRESS_ADDR_A0_0 == addr)
++ return 1;
++ return 0;
++}
++
++/**
++ * intel_qrk_spi_add_onboard_devs
++ *
++ * @return 0 on success or standard errnos on failure
++ *
++ * Registers onboard SPI device(s) present on the Izmir platform
++ */
++static int intel_qrk_spi_add_onboard_devs(void)
++{
++
++ return spi_register_board_info(spi_onboard_devs,
++ ARRAY_SIZE(spi_onboard_devs));
++}
++
++
++/**
++ * intel_qrk_gpio_restrict_probe
++ *
++ * Register devices that depend on GPIOs.
++ * Note this function makes extensive use of the probe deferral mechanism:
++ * gpio_request() for a GPIO that is not yet available returns
++ * -EPROBE_DEFER.
++ */
++static int intel_qrk_gpio_restrict_probe(struct platform_device *pdev)
++{
++ int ret = 0;
++ struct i2c_client *cypress = NULL, *eeprom = NULL;
++ static int spi_done;
++ static int gpios_done;
++
++ if (spi_done)
++ goto gpios;
++
++ ret = intel_qrk_spi_add_onboard_devs();
++ if (ret)
++ goto end;
++
++ spi_done = 1;
++
++gpios:
++ if (gpios_done)
++ goto i2c;
++
++ ret = gpio_request_array(reserved_gpios, ARRAY_SIZE(reserved_gpios));
++ if (ret)
++ goto end;
++
++ probed_i2c_cypress.irq = gpio_to_irq(GPIO_CYPRESS_INT_S0);
++
++ gpios_done = 1;
++
++i2c:
++ i2c_adap = i2c_get_adapter(0);
++ if (NULL == i2c_adap) {
++ pr_info("%s: i2c adapter not ready yet. Deferring..\n",
++ __func__);
++ ret = -EPROBE_DEFER;
++ goto end;
++ }
++ strlcpy(probed_i2c_cypress.type, "cy8c9540a", I2C_NAME_SIZE);
++ cypress = i2c_new_probed_device(i2c_adap, &probed_i2c_cypress,
++ cypress_i2c_addr, cypress_i2c_probe);
++ strlcpy(probed_i2c_eeprom.type, "at24", I2C_NAME_SIZE);
++ eeprom = i2c_new_probed_device(i2c_adap, &probed_i2c_eeprom,
++ eeprom_i2c_addr, eeprom_i2c_probe);
++ i2c_put_adapter(i2c_adap);
++
++ if (NULL == cypress || NULL == eeprom) {
++ pr_err("%s: can't probe Cypress Expander\n", __func__);
++ ret = -ENODEV;
++ goto end;
++ }
++
++end:
++ return ret;
++}
++
++static struct platform_driver gpio_restrict_pdriver = {
++ .driver = {
++ .name = GPIO_RESTRICT_NAME,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_gpio_restrict_probe,
++};
++
++static int intel_qrk_plat_galileo_probe(struct platform_device *pdev)
++{
++ int ret = 0;
++
++ /* Assign GIP driver handle for board-specific settings */
++ intel_qrk_gip_get_pdata = galileo_gip_get_pdata;
++
++ /* gpio */
++ ret = platform_driver_register(&gpio_restrict_pdriver);
++ if (ret)
++ goto end;
++
++#if 0
++ /* legacy SPI - TBD */
++ ret = platform_driver_register(&intel_qrk_plat_galileo_lpcspi_pdriver);
++ if (ret)
++ goto end;
++#endif
++end:
++ return ret;
++}
++
++static int intel_qrk_plat_galileo_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static struct platform_driver qrk_galileo_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_plat_galileo_probe,
++ .remove = intel_qrk_plat_galileo_remove,
++};
++
++module_platform_driver(qrk_galileo_driver);
++
++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>");
++MODULE_DESCRIPTION("Galileo BSP Data");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_ALIAS("platform:"DRIVER_NAME);
++
+diff --git a/drivers/platform/x86/quark/intel_qrk_plat_kips_bay.c b/drivers/platform/x86/quark/intel_qrk_plat_kips_bay.c
+new file mode 100644
+index 0000000..5e94b4b
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_plat_kips_bay.c
+@@ -0,0 +1,176 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark Legacy Platform Data Layout.conf accessor
++ *
++ * Simple Legacy SPI flash access layer
++ *
++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013
++ */
++
++#include <linux/errno.h>
++#include <linux/gpio.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/spi/pxa2xx_spi.h>
++#include <linux/spi/spi.h>
++
++#define DRIVER_NAME "KipsBay"
++#define GPIO_RESTRICT_NAME "qrk-gpio-restrict-sc"
++
++static int gpio_cs = 1;
++
++module_param(gpio_cs, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(gpio_cs, "Enable GPIO chip-select for SPI channel 1");
++
++
++/******************************************************************************
++ * Analog Devices AD7298 SPI Device Platform Data
++ ******************************************************************************/
++#include "linux/platform_data/ad7298.h"
++
++/* Maximum input voltage allowed for each ADC input, in milliVolts */
++#define AD7298_MAX_EXT_VIN 5000
++
++static const struct ad7298_platform_data ad7298_platform_data = {
++ .ext_ref = false,
++ .ext_vin_max = { AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN,
++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN }
++};
++
++/******************************************************************************
++ * Intel Quark SPI Controller Data
++ ******************************************************************************/
++static struct pxa2xx_spi_chip qrk_ffrd_spi_0_cs_0 = {
++ .gpio_cs = 8,
++};
++
++static struct pxa2xx_spi_chip qrk_ffrd_spi_1_cs_0 = {
++ .gpio_cs = 10,
++};
++
++static struct spi_board_info spi0_onboard_devs[] = {
++ {
++ .modalias = "ad7298",
++ .max_speed_hz = 5000000,
++ .platform_data = &ad7298_platform_data,
++ .mode = SPI_MODE_2,
++ .bus_num = 0,
++ .chip_select = 0,
++ .controller_data = &qrk_ffrd_spi_0_cs_0,
++ }
++};
++
++static struct spi_board_info spi1_onboard_devs_gpiocs[] = {
++ {
++ .modalias = "spidev",
++ .chip_select = 0,
++ .controller_data = NULL,
++ .max_speed_hz = 50000000,
++ .bus_num = 1,
++ .controller_data = &qrk_ffrd_spi_1_cs_0,
++ },
++};
++
++static struct spi_board_info spi1_onboard_devs[] = {
++ {
++ .modalias = "spidev",
++ .chip_select = 0,
++ .controller_data = NULL,
++ .max_speed_hz = 50000000,
++ .bus_num = 1,
++ },
++};
++
++/**
++ * intel_qrk_spi_add_onboard_devs
++ *
++ * @return 0 on success or standard errnos on failure
++ *
++ * Registers onboard SPI device(s) present on the Kips Bay platform
++ */
++static int intel_qrk_spi_add_onboard_devs(void)
++{
++ int ret = 0;
++
++ ret = spi_register_board_info(spi0_onboard_devs,
++ ARRAY_SIZE(spi0_onboard_devs));
++ if (ret)
++ return ret;
++
++ if (gpio_cs)
++ return spi_register_board_info(spi1_onboard_devs_gpiocs,
++ ARRAY_SIZE(spi1_onboard_devs_gpiocs));
++ else
++ return spi_register_board_info(spi1_onboard_devs,
++ ARRAY_SIZE(spi1_onboard_devs));
++}
++
++
++/**
++ * intel_qrk_gpio_restrict_probe
++ *
++ * Make GPIOs pertaining to Firmware inaccessible by requesting them. The
++ * GPIOs are never released nor accessed by this driver.
++ */
++static int intel_qrk_gpio_restrict_probe(struct platform_device *pdev)
++{
++ return intel_qrk_spi_add_onboard_devs();
++}
++
++static struct platform_driver gpio_restrict_pdriver = {
++ .driver = {
++ .name = GPIO_RESTRICT_NAME,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_gpio_restrict_probe,
++};
++
++static int intel_qrk_plat_kips_bay_probe(struct platform_device *pdev)
++{
++ return platform_driver_register(&gpio_restrict_pdriver);
++}
++
++static int intel_qrk_plat_kips_bay_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static struct platform_driver qrk_kips_bay_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_plat_kips_bay_probe,
++ .remove = intel_qrk_plat_kips_bay_remove,
++};
++
++module_platform_driver(qrk_kips_bay_driver);
++
++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>");
++MODULE_DESCRIPTION("Kips Bay BSP Data");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_ALIAS("platform:"DRIVER_NAME);
++
+diff --git a/drivers/platform/x86/quark/intel_qrk_sb.c b/drivers/platform/x86/quark/intel_qrk_sb.c
+new file mode 100644
+index 0000000..658c41f
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_sb.c
+@@ -0,0 +1,253 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark side-band driver
++ *
++ * Thread-safe sideband read/write routine.
++ *
++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2012
++ */
++
++#include <linux/errno.h>
++#include <linux/intel_qrk_sb.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/spinlock.h>
++#include <linux/pci.h>
++#include "intel_qrk_imr.h"
++
++#define INTEL_QRK_SB_CMD_ADDR (0x000000D0)
++#define INTEL_QRK_SB_DATA_ADDR (0x000000D4)
++
++#define INTEL_QRK_SB_MCR_SHIFT (24)
++#define INTEL_QRK_SB_PORT_SHIFT (16)
++#define INTEL_QRK_SB_REG_SHIFT (8)
++#define INTEL_QRK_SB_BYTEEN (0xF0) /* enable all 32 bits */
++
++/* Simple structure for module */
++struct intel_qrk_sb_dev{
++ struct pci_dev * pdev;
++ spinlock_t slock;
++ u8 initialized;
++};
++
++static struct intel_qrk_sb_dev sb_dev = {
++ .initialized = 0
++};
++
++/* Dependant drivers */
++static struct platform_device pdev [] = {
++ {
++ .name = "intel-qrk-esram",
++ },
++ {
++ .name = "intel-qrk-ecc",
++ },
++ {
++ .name = "intel-qrk-thrm",
++ },
++};
++
++/**
++ * intel_qrk_sb_read_reg
++ *
++ * @param qrk_sb_id: Sideband identifier
++ * @param command: Command to send to destination identifier
++ * @param reg: Target register w/r to qrk_sb_id
++ * @return nothing
++ *
++ * Utility function to allow thread-safe read of side-band
++ * command - can be different read op-code types - which is why we don't
++ * hard-code this value directly into msg
++ */
++void intel_qrk_sb_read_reg(qrk_sb_id id, u8 cmd, u8 reg, u32 *data, u8 lock)
++{
++ u32 msg = (cmd << INTEL_QRK_SB_MCR_SHIFT) |
++ ((id << INTEL_QRK_SB_PORT_SHIFT) & 0xFF0000)|
++ ((reg << INTEL_QRK_SB_REG_SHIFT) & 0xFF00)|
++ INTEL_QRK_SB_BYTEEN;
++
++ if(data == NULL)
++ return;
++
++ if (likely(lock == 1)) {
++ spin_lock(&sb_dev.slock);
++ }
++
++ pci_write_config_dword(sb_dev.pdev, INTEL_QRK_SB_CMD_ADDR, msg);
++ pci_read_config_dword(sb_dev.pdev, INTEL_QRK_SB_DATA_ADDR, data);
++
++ if(likely(lock == 1)){
++ spin_unlock(&sb_dev.slock);
++ }
++
++}
++EXPORT_SYMBOL(intel_qrk_sb_read_reg);
++
++/**
++ * intel_qrk_sb_write_reg
++ *
++ * @param qrk_sb_id: Sideband identifier
++ * @param command: Command to send to destination identifier
++ * @param reg: Target register w/r to qrk_sb_id
++ * @return nothing
++ *
++ * Utility function to allow thread-safe write of side-band
++ */
++void intel_qrk_sb_write_reg(qrk_sb_id id, u8 cmd, u8 reg, u32 data, u8 lock)
++{
++ u32 msg = (cmd << INTEL_QRK_SB_MCR_SHIFT) |
++ ((id << INTEL_QRK_SB_PORT_SHIFT) & 0xFF0000)|
++ ((reg << INTEL_QRK_SB_REG_SHIFT) & 0xFF00)|
++ INTEL_QRK_SB_BYTEEN;
++
++ if(likely(lock == 1)){
++ spin_lock(&sb_dev.slock);
++ }
++
++ pci_write_config_dword(sb_dev.pdev, INTEL_QRK_SB_DATA_ADDR, data);
++ pci_write_config_dword(sb_dev.pdev, INTEL_QRK_SB_CMD_ADDR, msg);
++
++ if(likely(lock == 1)){
++ spin_unlock(&sb_dev.slock);
++ }
++}
++EXPORT_SYMBOL(intel_qrk_sb_write_reg);
++
++/**
++ * intel_qrk_sb_runfn_lock
++ *
++ * @param fn: Callback function - which requires side-band spinlock and !irq
++ * @param arg: Callback argument
++ * @return 0 on success < 0 on failure
++ *
++ * Runs the given function pointer inside of a call to the local spinlock using
++ * spin_lock_irqsave/spin_unlock_irqrestore. Needed for the eSRAMv1 driver to
++ * guarantee atomicity, but, available to any other user of sideband provided
++ * rules are respected.
++ * Rules:
++ * fn may not sleep
++ * fn may not change the state of irqs
++ */
++int intel_qrk_sb_runfn_lock(int (*fn)( void * arg ), void * arg)
++{
++ unsigned long flags = 0;
++ int ret = 0;
++
++ if(unlikely(fn == NULL)){
++ return -EINVAL;
++ }
++
++ /* Get spinlock with IRQs off */
++ spin_lock_irqsave(&sb_dev.slock, flags);
++
++ /* Run function atomically */
++ ret = fn(arg);
++
++ /* Release lock */
++ spin_unlock_irqrestore(&sb_dev.slock, flags);
++
++ return ret;
++}
++EXPORT_SYMBOL(intel_qrk_sb_runfn_lock);
++
++/**
++ * sb_probe
++ *
++ * @param dev: the PCI device matching
++ * @param id: entry in the match table
++ * @return 0
++ *
++ * Callback from PCI layer when dev/vendor ids match.
++ * Sets up necessary resources
++ */
++static int intel_qrk_sb_probe(struct pci_dev *dev, const struct pci_device_id *id)
++{
++ int i = 0;
++
++ /* Init struct */
++ memset(&sb_dev, 0x00, sizeof(sb_dev));
++
++ /* Hook device */
++ sb_dev.pdev = dev;
++
++ /* Init locking structures */
++ spin_lock_init(&sb_dev.slock);
++
++ /* Set state */
++ sb_dev.initialized = 1;
++
++ /* Register side-band sub-ordinate drivers */
++ for (i = 0; i < sizeof(pdev)/sizeof(struct platform_device); i++){
++ /* Register side-band sub-ordinate drivers */
++ platform_device_register(&pdev[i]);
++ }
++ pr_info("Intel Quark side-band driver registered\n");
++
++ /* Switch off boot-time IMRs nice and early */
++ return intel_qrk_imr_init(dev->device);
++}
++
++/**
++ * sb_remove
++ *
++ * @param pdev: PCI device
++ * @return nothing
++ *
++ * Callback from PCI sub-system upon PCI dev removal
++ */
++static void intel_qrk_sb_remove(struct pci_dev *pdev)
++{
++}
++
++/* Quark hardware */
++struct pci_device_id intel_qrk_sb_ids[] = {
++ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_X1000_HOST_BRIDGE), 0},
++ { PCI_VDEVICE(INTEL, 0x12C0), 0},
++ { 0 }
++};
++
++MODULE_DEVICE_TABLE(pci, intel_qrk_sb_ids);
++
++/* PCI callbacks */
++static struct pci_driver intel_qrk_sb_driver = {
++ .name = "intel_qrk_sb",
++ .id_table = intel_qrk_sb_ids,
++ .probe = intel_qrk_sb_probe,
++ .remove = intel_qrk_sb_remove,
++};
++
++/**
++ * intel_qrk_sb_init
++ *
++ * Module entry point
++ */
++static int __init intel_qrk_sb_init(void)
++{
++ return pci_register_driver(&intel_qrk_sb_driver);
++}
++
++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>");
++MODULE_DESCRIPTION("Intel Quark SOC side-band driver");
++MODULE_LICENSE("Dual BSD/GPL");
++
++/* Initialise early since other drivers eSRAM, DRAM ECC and thermal depend */
++subsys_initcall(intel_qrk_sb_init);
+diff --git a/drivers/platform/x86/quark/intel_qrk_thermal.c b/drivers/platform/x86/quark/intel_qrk_thermal.c
+new file mode 100644
+index 0000000..d603d8b
+--- /dev/null
++++ b/drivers/platform/x86/quark/intel_qrk_thermal.c
+@@ -0,0 +1,360 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark Thermal driver
++ */
++#include <linux/err.h>
++#include <linux/fs.h>
++#include <linux/intel_qrk_sb.h>
++#include <linux/list.h>
++#include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/printk.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/thermal.h>
++#include <linux/timer.h>
++
++#define DRIVER_NAME "intel-qrk-thrm"
++
++/* Definition of register locations for thermal management */
++#define THRM_CTRL_REG (0x80) /* Thermal control */
++#define THRM_MODE_REG (0xB0) /* Thermal mode */
++#define THRM_MODE_SENSOR_EN (0x00008000) /* Thermal mode sensor enable */
++#define THRM_TEMP_REG (0xB1) /* Thermal sensor temperature */
++#define THRM_TRPCLR_REG (0xB2) /* Catastropic/Hot trip/clear */
++#define THRM_AUXTRP_REG (0xB3) /* Aux0-Aux3 trip point */
++#define THRM_AUXCLR_REG (0xB4) /* Aux0-Aux3 clear trip */
++#define THRM_STATUS_REG (0xB5) /* Thermal sensor status */
++#define THRM_TRIPBEHAVE_REG (0xB6) /* Trip point behavior */
++#define THRM_MSIADDR_REG (0xC5) /* Thermal MSI addres reg */
++#define THRM_MSIDATA_REG (0xC6) /* Thermal MSI data reg */
++#define THRM_CTRL_READ (0x10) /* Config reg */
++#define THRM_CTRL_WRITE (0x11) /* Config reg */
++
++#define SOC_TSENSOR_REG (0x34)
++#define SOC_TSENSOR_RST (0x00000001)
++#define SOC_CTRL_READ (0x06)
++#define SOC_CTRL_WRITE (0x07)
++
++
++#define THRM_ZONE_COUNT 2 /* Only hot/critical relevant */
++#define ACTIVE_INTERVAL (1000)
++#define IDLE_INTERVAL (20000)
++#define MCELSIUS(x) ((x) * 1000)
++
++/* CPU Zone information */
++#define CATASTROPIC_ZONE 0
++#define HOT_ZONE 1
++#define AUX0_ZONE 2 /* Unused */
++#define AUX1_ZONE 3 /* Unused */
++#define AUX2_ZONE 4 /* Unused */
++#define AUX3_ZONE 5 /* Unused */
++#define MIN_USED_ZONE CATASTROPIC_ZONE
++#define MAX_USED_ZONE HOT_ZONE
++/*
++ * Default catastrophic/hot trip values - in degrees celsius
++ * Maximum temperature is 105 degrees
++ */
++#define CRIT_TEMP 104
++#define HOT_TEMP 95
++#define RAW2CELSIUS_DIFF 50
++
++static int driver_enable = 1;
++module_param(driver_enable, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(driver_enable, "Disable Thermal Driver Polling");
++
++/* Shorten fn names to fit 80 char limit */
++#ifndef sb_read
++#define sb_read intel_qrk_sb_read_reg
++#endif
++#ifndef sb_write
++#define sb_write intel_qrk_sb_write_reg
++#endif
++
++struct intel_qrk_therm_zone {
++ enum thermal_trip_type type;
++ int trip_value;
++};
++
++/**
++ * struct intel_qrk_thermal_dev
++ *
++ */
++struct intel_qrk_thermal_dev {
++ enum thermal_device_mode mode;
++ struct intel_qrk_therm_zone tzone[THRM_ZONE_COUNT];
++ struct mutex lock;
++ struct platform_device *pldev; /* Platform device */
++ struct thermal_zone_device *therm_dev; /* Thermal device */
++};
++
++static struct intel_qrk_thermal_dev qrk_tdev;
++
++/******************************************************************************
++ * Thermal API implementation
++ ******************************************************************************/
++
++/**
++ * get_temp
++ *
++ * @param tz: Thermal zone descriptor
++ *
++ * Get the current temperature
++ * We have exactly one thermal zone/sensor
++ * Value passed is an unsigned long - our sensor reports up to -50 celsius so we
++ * just clip at zero if the temperature is negative.
++ */
++static int intel_qrk_thermal_get_temp(struct thermal_zone_device *tz,
++ unsigned long *temp)
++{
++ sb_read(SB_ID_THERMAL, THRM_CTRL_READ, THRM_TEMP_REG, (u32 *)temp, 1);
++ *temp -= RAW2CELSIUS_DIFF;
++
++ /* Clip to unsigned output value if sensor is reporting sub-zero */
++ if ((int)*temp < 0)
++ *temp = 0;
++
++ *temp = MCELSIUS(*temp&0x000000FF);
++
++ return 0;
++}
++
++/**
++ * get_trend
++ *
++ * Wears good clothes
++ */
++static int intel_qrk_thermal_get_trend(struct thermal_zone_device *tz,
++ int trip, enum thermal_trend *trend)
++{
++ if (tz->temperature >= trip)
++ *trend = THERMAL_TREND_RAISING;
++ else
++ *trend = THERMAL_TREND_DROPPING;
++
++ return 0;
++}
++
++/**
++ * intel_qrk_thermal_get_mode
++ *
++ * Get the mode
++ */
++static int intel_qrk_thermal_get_mode(struct thermal_zone_device *tz,
++ enum thermal_device_mode *mode)
++{
++ mutex_lock(&qrk_tdev.lock);
++ *mode = qrk_tdev.mode;
++ mutex_unlock(&qrk_tdev.lock);
++
++ return 0;
++}
++
++/**
++ * intel_qrk_thermal_set_mode
++ *
++ * Set the mode
++ */
++static int intel_qrk_thermal_set_mode(struct thermal_zone_device *tz,
++ enum thermal_device_mode mode)
++{
++ mutex_lock(&qrk_tdev.lock);
++
++ if (mode == THERMAL_DEVICE_ENABLED)
++ qrk_tdev.therm_dev->polling_delay = IDLE_INTERVAL;
++ else
++ qrk_tdev.therm_dev->polling_delay = 0;
++ qrk_tdev.mode = mode;
++
++ mutex_unlock(&qrk_tdev.lock);
++
++ thermal_zone_device_update(qrk_tdev.therm_dev);
++ pr_info("thermal polling set for duration=%d msec\n",
++ qrk_tdev.therm_dev->polling_delay);
++ return 0;
++}
++
++/**
++ * intel_qrk_thermal_get_trip_type
++ *
++ * Get trip type
++ */
++static int intel_qrk_thermal_get_trip_type(struct thermal_zone_device *tz,
++ int trip, enum thermal_trip_type *type)
++{
++ if (trip < MIN_USED_ZONE || trip > MAX_USED_ZONE)
++ return -EINVAL;
++
++ *type = qrk_tdev.tzone[trip].type;
++ return 0;
++}
++
++/**
++ * intel_qrk_thermal_get_trip_temp
++ *
++ * Get trip temp
++ */
++static int intel_qrk_thermal_get_trip_temp(struct thermal_zone_device *tz,
++ int trip, unsigned long *temp)
++{
++ if (trip < MIN_USED_ZONE || trip > MAX_USED_ZONE)
++ return -EINVAL;
++
++ /* Convert the temperature into millicelsius */
++ *temp = qrk_tdev.tzone[trip].trip_value;
++
++ return 0;
++}
++
++/**
++ * intel_qrk_thermal_get_trip_type
++ *
++ * Get trip temp
++ */
++static int intel_qrk_thermal_get_crit_temp(struct thermal_zone_device *tz,
++ unsigned long *temp)
++{
++ /* Critical zone */
++ *temp = qrk_tdev.tzone[CATASTROPIC_ZONE].trip_value;
++ return 0;
++}
++
++static struct thermal_zone_device_ops intel_qrk_thrm_dev_ops = {
++ .get_temp = intel_qrk_thermal_get_temp,
++ .get_trend = intel_qrk_thermal_get_trend,
++ .get_mode = intel_qrk_thermal_get_mode,
++ .set_mode = intel_qrk_thermal_set_mode,
++ .get_trip_type = intel_qrk_thermal_get_trip_type,
++ .get_trip_temp = intel_qrk_thermal_get_trip_temp,
++ .get_crit_temp = intel_qrk_thermal_get_crit_temp,
++};
++
++
++
++/**
++ * intel_qrk_init_zone
++ *
++ * Initialise a zone
++ */
++static void intel_qrk_thermal_init_zone(struct intel_qrk_therm_zone *tz,
++ enum thermal_trip_type type, int trip_value)
++{
++ tz->type = type;
++ tz->trip_value = MCELSIUS(trip_value);
++}
++
++/******************************************************************************
++ * Module Entry/Exit hooks
++ ******************************************************************************/
++
++/**
++ * intel_qrk_thermal_probe
++ *
++ * @param pdev: Platform device
++ * @return 0 success < 0 failure
++ *
++ * Callback from platform sub-system to probe
++ *
++ * This routine registers a thermal device with the kernel's thermal management
++ * sub-system
++ */
++static int intel_qrk_thermal_probe(struct platform_device *pdev)
++{
++ int err = 0;
++ int critical_temp = 0, hot_temp = 0;
++ uint32_t regval = 0;
++
++ if (driver_enable == 0)
++ return 0;
++
++ memset(&qrk_tdev, 0x00, sizeof(qrk_tdev));
++
++ critical_temp = CRIT_TEMP;
++ hot_temp = HOT_TEMP;
++
++ /* Enumerate zone type data */
++ memset(&qrk_tdev, 0x00, sizeof(qrk_tdev));
++ mutex_init(&qrk_tdev.lock);
++
++ /* Set initial state disabled */
++ qrk_tdev.mode = THERMAL_DEVICE_ENABLED;
++
++ intel_qrk_thermal_init_zone(&qrk_tdev.tzone[CATASTROPIC_ZONE],
++ THERMAL_TRIP_CRITICAL, critical_temp);
++ intel_qrk_thermal_init_zone(&qrk_tdev.tzone[HOT_ZONE],
++ THERMAL_TRIP_HOT, hot_temp);
++
++ /* Register a thermal zone */
++ qrk_tdev.therm_dev = thermal_zone_device_register(DRIVER_NAME,
++ THRM_ZONE_COUNT, 0, 0, &intel_qrk_thrm_dev_ops,
++ 0, IDLE_INTERVAL, ACTIVE_INTERVAL);
++
++ if (IS_ERR(qrk_tdev.therm_dev)) {
++ err = PTR_ERR(qrk_tdev.therm_dev);
++ return err;
++ }
++
++ /* Read the BIOS configured hardware catastrophic trip temp */
++ sb_read(SB_ID_THERMAL, THRM_CTRL_READ, THRM_TRPCLR_REG, &regval, 1);
++ regval = (regval & 0xff) - 50;
++
++ pr_info("THRM: critical reset %d c hot %d c hardware failover %d c\n",
++ critical_temp, hot_temp, regval);
++
++ return 0;
++}
++
++/**
++ * intel_qrk_thermal_remove
++ *
++ * @return 0 success < 0 failure
++ *
++ * Removes a platform device
++ */
++static int intel_qrk_thermal_remove(struct platform_device *pdev)
++{
++ if (qrk_tdev.therm_dev != NULL) {
++ thermal_zone_device_unregister(qrk_tdev.therm_dev);
++ return 0;
++ }
++ return -EINVAL;
++}
++
++/*
++ * Platform structures useful for interface to PM subsystem
++ */
++static struct platform_driver intel_qrk_thermal_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ },
++ .probe = intel_qrk_thermal_probe,
++ .remove = intel_qrk_thermal_remove,
++};
++
++module_platform_driver(intel_qrk_thermal_driver);
++
++
++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>");
++MODULE_DESCRIPTION("Intel Quark Thermal driver");
++MODULE_LICENSE("Dual BSD/GPL");
+diff --git a/include/linux/intel_qrk_sb.h b/include/linux/intel_qrk_sb.h
+new file mode 100644
+index 0000000..65d9b5e
+--- /dev/null
++++ b/include/linux/intel_qrk_sb.h
+@@ -0,0 +1,92 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark side-band driver
++ *
++ * Thread-safe sideband read/write routine.
++ *
++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2012
++ */
++
++#ifndef __INTEL_QRK_SB_H__
++#define __INTEL_QRK_SB_H__
++
++#include <linux/types.h>
++
++#define PCI_DEVICE_ID_X1000_HOST_BRIDGE 0x0958
++
++typedef enum {
++ SB_ID_HUNIT = 0x03,
++ SB_ID_THERMAL = 0x04,
++ SB_ID_ESRAM = 0x05,
++ SB_ID_SOC = 0x31,
++}qrk_sb_id;
++
++/**
++ * intel_qrk_sb_read_reg
++ *
++ * @param qrk_sb_id: Sideband identifier
++ * @param command: Command to send to destination identifier
++ * @param reg: Target register w/r to qrk_sb_id
++ * @return nothing
++ *
++ * Utility function to allow thread-safe read of side-band
++ * command - can be different read op-code types - which is why we don't
++ * hard-code this value directly into msg
++ */
++void intel_qrk_sb_read_reg(qrk_sb_id id, u8 cmd, u8 reg, u32 *data, u8 lock);
++
++/**
++ * intel_qrk_sb_write_reg
++ *
++ * @param qrk_sb_id: Sideband identifier
++ * @param command: Command to send to destination identifier
++ * @param reg: Target register w/r to qrk_sb_id
++ * @return nothing
++ *
++ * Utility function to allow thread-safe write of side-band
++ */
++void intel_qrk_sb_write_reg(qrk_sb_id id, u8 cmd, u8 reg, u32 data, u8 lock);
++
++/**
++ * intel_qrk_sb_runfn_lock
++ *
++ * @param fn: Callback function - which requires side-band spinlock and !irq
++ * @param arg: Callback argument
++ * @return 0 on success < 0 on failure
++ *
++ * Runs the given function pointer inside of a call to the local spinlock using
++ * spin_lock_irqsave/spin_unlock_irqrestore. Needed for the eSRAMv1 driver to
++ * guarantee atomicity, but, available to any other user of sideband provided
++ * rules are respected.
++ * Rules:
++ * fn may not sleep
++ * fn may not change the state of irqs
++ */
++int intel_qrk_sb_runfn_lock(int (*fn)( void * arg ), void * arg);
++
++/**
++ * intel_qrk_sb_initialized
++ *
++ * False if sideband running on non-Quark system
++ */
++int intel_qrk_sb_initialized(void);
++
++#endif /* __INTEL_QRK_SB_H__ */
+diff --git a/include/linux/platform_data/quark.h b/include/linux/platform_data/quark.h
+new file mode 100644
+index 0000000..85f5508
+--- /dev/null
++++ b/include/linux/platform_data/quark.h
+@@ -0,0 +1,44 @@
++/*
++ * Copyright(c) 2013 Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Contact Information:
++ * Intel Corporation
++ */
++/*
++ * Intel Quark platform data definition
++ */
++
++#ifndef _PDATA_QUARK_H
++#define _PDATA_QUARK_H
++
++typedef enum {
++ QUARK_PLAT_UNDEFINED = 0,
++ QUARK_EMULATION = 1,
++ QUARK_PEAK = 2,
++ KIPS_BAY = 3,
++ CROSS_HILL = 4,
++ QUARK_HILL = 5,
++ GALILEO = 6,
++}qrk_plat_id_t;
++
++typedef enum {
++ PLAT_DATA_ID = 1,
++ PLAT_DATA_SN = 2,
++ PLAT_DATA_MAC0 = 3,
++ PLAT_DATA_MAC1 = 4,
++}plat_dataid_t;
++
++#endif /* _PDATA_QUARK_H */
+--
+1.7.4.1
+