Loki: Implement mcu debugger in Kernel
Will Wu [Wed, 27 Nov 2013 02:54:46 +0000 (10:54 +0800)]
The debugger runing in kernel is used to generate
required time sequence to C2 debug interface of the
microcontroller in joystick.

Bug 1375728

Change-Id: I7e366068faa16745e46fe60826b92c9ed44efb32
Signed-off-by: Will Wu <willw@nvidia.com>
Reviewed-on: http://git-master/r/335983
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>

drivers/misc/c2port/Kconfig
drivers/misc/c2port/Makefile
drivers/misc/c2port/c2port-loki.c [new file with mode: 0644]
drivers/misc/c2port/core.c
include/linux/c2port.h
include/linux/platform_data/tegra_c2port_platform_data.h [new file with mode: 0644]

index 0dd690e..5768d66 100644 (file)
@@ -20,6 +20,17 @@ menuconfig C2PORT
 
 if C2PORT
 
+config C2PORT_LOKI
+       tristate "C2 port support for NVIDIA Loki"
+       default n
+       depends on MACH_LOKI
+       help
+         This option enables C2 support for Joystick micro controller of
+         NVIDIA Loki platform.
+
+         To compile this driver as a module, choose M here: the module will
+         be called c2port-loki.
+
 config C2PORT_DURAMAR_2150
        tristate "C2 port support for Eurotech's Duramar 2150"
        depends on X86
index 3b2cf43..65da1b2 100644 (file)
@@ -1,3 +1,5 @@
 obj-$(CONFIG_C2PORT)           += core.o
+obj-$(CONFIG_C2PORT_LOKI)      += c2port-loki.o
 
 obj-$(CONFIG_C2PORT_DURAMAR_2150)      += c2port-duramar2150.o
+
diff --git a/drivers/misc/c2port/c2port-loki.c b/drivers/misc/c2port/c2port-loki.c
new file mode 100644 (file)
index 0000000..ec15471
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/time.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/c2port.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/platform_data/tegra_c2port_platform_data.h>
+#include <asm/system.h>
+
+struct tegra_c2port_device {
+       struct c2port_device *p_c2dev;
+       /* configurations */
+       u8 GPIO_C2CK;
+       u8 GPIO_C2D;
+};
+
+static struct tegra_c2port_device *g_c2port_device;
+
+/* Indicating the status of C2 interface */
+static bool g_gpio_required;
+
+/* Enable gpios */
+/*
+ * We need Hall sensor's gpio usage to be diabled
+ * for requesting it as C2D pin.
+ * After finishing using C2 interface,  we will reset
+ * it.
+ */
+static void enable_hall_sensor_gpio(int gpio_c2d)
+{
+       gpio_request(gpio_c2d, "Hall Effect Sensor");
+       gpio_direction_input(gpio_c2d);
+       enable_irq(gpio_to_irq(gpio_c2d));
+}
+
+static void disable_hall_sensor_gpio(int gpio_c2d)
+{
+       disable_irq(gpio_to_irq(gpio_c2d));
+       gpio_free(gpio_c2d);
+}
+
+/* if GPIO_C2D is occupied, then need disable Hall Sensor */
+static void acquire_gpio(void)
+{
+       int ret;
+       struct tegra_c2port_device *pc2dev = g_c2port_device;
+       struct device *dev = pc2dev->p_c2dev->dev;
+
+       if (g_gpio_required) {
+               dev_info(dev, "Kernel GPIO alread been reuqired\n");
+               return;
+       }
+       /* C2CK request should not be failed */
+       ret = gpio_request(pc2dev->GPIO_C2CK, "GPIO_C2CK");
+       if (ret < 0) {
+               dev_err(dev, "GPIO_C2CK request failed %d\n", ret);
+               return;
+       }
+       gpio_direction_output(pc2dev->GPIO_C2CK, 1);
+       ret = gpio_request(pc2dev->GPIO_C2D, "GPIO_C2D");
+       if (ret < 0) {
+               /* Free GPIO and IRQ occupied by Hall Sensor */
+               disable_hall_sensor_gpio(pc2dev->GPIO_C2D);
+               ret = gpio_request(pc2dev->GPIO_C2D, "GPIO_C2D");
+       }
+       if (ret < 0) {
+               dev_err(dev,
+                       "GPIO_C2D reqest failed,need disable HallSensor!\n");
+               return;
+       }
+       gpio_direction_input(pc2dev->GPIO_C2D);
+       g_gpio_required = true;
+}
+
+static void release_gpio(void)
+{
+       struct tegra_c2port_device *pc2dev = g_c2port_device;
+       if (g_gpio_required == false)
+               return;
+       gpio_free(pc2dev->GPIO_C2CK);
+       gpio_free(pc2dev->GPIO_C2D);
+       enable_hall_sensor_gpio(pc2dev->GPIO_C2D);
+       g_gpio_required = false;
+}
+
+/* operations to manipulate C2 interfaces */
+/* enable c2 port access, status = 1 means set them as output */
+static void tegra_c2port_access(struct c2port_device *dev, int status)
+{
+       if (status) {
+               /* enable */
+               acquire_gpio();
+               if (!g_gpio_required)
+                       dev_err(dev->dev, "GPIO can't be acquired!\n");
+       } else {
+               /* disable */
+               release_gpio();
+               if (g_gpio_required)
+                       dev_err(dev->dev, "GPIO can't be released\n");
+       }
+}
+
+/* set c2d direction, 0 = output, 1 = input */
+static void tegra_c2port_c2d_dir(struct c2port_device *dev, int direction)
+{
+       struct tegra_c2port_device *pc2dev = g_c2port_device;
+       if (!g_gpio_required)
+               dev_err(dev->dev, "GPIO hasn't be acquired!\n");
+       if (!direction)
+               gpio_direction_output(pc2dev->GPIO_C2D, 1);
+       else
+               gpio_direction_input(pc2dev->GPIO_C2D);
+}
+
+/* get c2d value */
+static int tegra_c2port_c2d_get(struct c2port_device *dev)
+{
+       int ret = 0;
+       struct tegra_c2port_device *pc2dev = g_c2port_device;
+       if (!g_gpio_required)
+               dev_err(dev->dev, "GPIO hasn't be acquired!\n");
+       ret = gpio_get_value(pc2dev->GPIO_C2D);
+       return ret;
+}
+
+/* set c2d value */
+static void tegra_c2port_c2d_set(struct c2port_device *dev, int value)
+{
+       struct tegra_c2port_device *pc2dev = g_c2port_device;
+       if (unlikely(!g_gpio_required))
+               dev_err(dev->dev, "GPIO hasn't be acquired!\n");
+       gpio_set_value(pc2dev->GPIO_C2D, value);
+}
+
+/* set c2ck value */
+static void tegra_c2port_c2ck_set(struct c2port_device *dev, int value)
+{
+       struct tegra_c2port_device *pc2dev = g_c2port_device;
+       /*
+        * We don't complain when gpio not required, because in core,
+        * c2ck_set is called before access function in c2_core
+        */
+       if ((!g_gpio_required))
+               return;
+       gpio_set_value(pc2dev->GPIO_C2CK, value);
+}
+
+static struct c2port_ops tegra_c2port_ops = {
+       .block_size = 512,
+       .blocks_num = 30,
+       .ram_size = 0x100,
+       /* although sfr_start should be 0x80 */
+       /* set it to 0x00 doesn't really matter */
+       .sfr_size = 0x100,
+       .xram_size = 0x800,
+       .access = tegra_c2port_access,
+       .c2d_dir = tegra_c2port_c2d_dir,
+       .c2d_get = tegra_c2port_c2d_get,
+       .c2d_set = tegra_c2port_c2d_set,
+       .c2ck_set = tegra_c2port_c2ck_set,
+};
+
+/* mcu_debugger driver functions */
+static int tegra_c2port_probe(struct platform_device *pdev)
+{
+       struct tegra_c2port_platform_data *pdata =
+           (struct tegra_c2port_platform_data *)pdev->dev.platform_data;
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "no platform_data\n");
+               return -ENOENT;
+       }
+
+       if (g_c2port_device) {
+               dev_err(&pdev->dev,
+                       "tegra_c2port probe more than one device!\n");
+               return -1;
+       }
+
+       g_c2port_device = devm_kzalloc(&pdev->dev,
+               sizeof(struct tegra_c2port_device), GFP_KERNEL);
+
+       if (!g_c2port_device) {
+               dev_err(&pdev->dev,
+                       "tegra_c2port_device allocated error!\n");
+               return -ENOMEM;
+       }
+       g_c2port_device->GPIO_C2CK = pdata->gpio_c2ck;
+       g_c2port_device->GPIO_C2D = pdata->gpio_c2d;
+
+       /* register the device with c2 port core */
+       g_c2port_device->p_c2dev = c2port_device_register("tegra_mcu_c2port",
+                                                         &tegra_c2port_ops,
+                                                         NULL);
+
+       if (!g_c2port_device->p_c2dev) {
+               dev_err(&pdev->dev,
+                       "tegra_port c2 device register failed!\n");
+               devm_kfree(&pdev->dev, g_c2port_device);
+               g_c2port_device = NULL;
+               return -EBUSY;
+       }
+       return 0;
+}
+
+static int tegra_c2port_remove(struct platform_device *pdev)
+{
+       /* remove c2 port device */
+       if (g_c2port_device) {
+               c2port_device_unregister(g_c2port_device->p_c2dev);
+               devm_kfree(&pdev->dev, g_c2port_device);
+       }
+       g_c2port_device = NULL;
+       return 0;
+}
+
+static struct of_device_id tegra_c2port_of_match[] = {
+       {.compatible = "tegra_c2port",},
+       {},
+};
+
+static struct platform_driver tegra_c2port_driver = {
+       .probe = tegra_c2port_probe,
+       .remove = tegra_c2port_remove,
+       .driver = {
+                  .name = "tegra_c2port",
+                  .owner = THIS_MODULE,
+                  .of_match_table = of_match_ptr(tegra_c2port_of_match),
+               }
+};
+
+module_platform_driver(tegra_c2port_driver);
+
+MODULE_AUTHOR("Will Wu <willw@nvidia.com>");
+MODULE_DESCRIPTION("C2 port driver for Loki");
+MODULE_LICENSE("GPLv2");
index f32550a..bee5792 100644 (file)
@@ -3,6 +3,7 @@
  *
  *  Copyright (c) 2007 Rodolfo Giometti <giometti@linux.it>
  *  Copyright (c) 2007 Eurotech S.p.A. <info@eurotech.it>
+ *  Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published by
@@ -45,6 +46,9 @@ static struct class *c2port_class;
 #define C2PORT_REVID           0x01
 #define C2PORT_FPCTL           0x02
 #define C2PORT_FPDAT           0xB4
+#define C2PORT_XRAMD           0x84
+#define C2PORT_XRAMAL          0xAD
+#define C2PORT_XRAMAH          0xC7
 
 /* C2 interface commands */
 #define C2PORT_GET_VERSION     0x01
@@ -52,6 +56,10 @@ static struct class *c2port_class;
 #define C2PORT_BLOCK_READ      0x06
 #define C2PORT_BLOCK_WRITE     0x07
 #define C2PORT_PAGE_ERASE      0x08
+#define C2PORT_DIRECT_READ     0x09
+#define C2PORT_DIRECT_WRITE    0x0A
+#define C2PORT_INDIRECT_READ   0x0B
+#define C2PORT_INDIRECT_WRITE  0x0C
 
 /* C2 status return codes */
 #define C2PORT_INVALID_COMMAND 0x00
@@ -537,6 +545,61 @@ static ssize_t c2port_store_flash_access(struct device *dev,
        return count;
 }
 
+int mcu_halted(struct c2port_device *c2dev)
+{
+       u8 status;
+       c2port_write_ar(c2dev, C2PORT_FPCTL);
+       c2port_read_dr(c2dev, &status);
+       if (status & 0x01)
+               return 1;
+       return 0;
+}
+
+static ssize_t c2port_show_ram_access(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct c2port_device *c2dev = dev_get_drvdata(dev);
+       return sprintf(buf, "%d\n", mcu_halted(c2dev));
+}
+
+static ssize_t c2port_store_ram_access(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       ssize_t ret;
+       int status;
+       struct c2port_device *c2dev = dev_get_drvdata(dev);
+       ret = sscanf(buf, "%d", &status);
+       if (ret != 1) {
+               dev_err(dev, "Invalid access parameter!\n");
+               return -EINVAL;
+       }
+       mutex_lock(&c2dev->mutex);
+       c2port_write_ar(c2dev, C2PORT_FPCTL);
+       /* Halt device */
+       if (status == 1)
+               c2port_write_dr(c2dev, 0x01);
+       else
+               c2port_write_dr(c2dev, 0x00);
+       mutex_unlock(&c2dev->mutex);
+       return count;
+}
+
+static ssize_t c2port_store_rest_halt_access(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct c2port_device *c2dev = dev_get_drvdata(dev);
+
+       c2port_reset(c2dev);
+       c2port_write_ar(c2dev, C2PORT_FPCTL);
+       c2port_write_dr(c2dev, 0x02);
+       c2port_write_dr(c2dev, 0x04);
+       c2port_write_dr(c2dev, 0x01);
+
+       return count;
+}
+
 static ssize_t __c2port_write_flash_erase(struct c2port_device *dev)
 {
        u8 status;
@@ -847,6 +910,348 @@ static ssize_t c2port_write_flash_data(struct file *filp, struct kobject *kobj,
        return ret;
 }
 
+/* helper function to implement ram/sfr/xram read/write */
+/* is_8bit is used for detect overflow in address format*/
+static inline bool is_8bit(u16 word)
+{
+       if (word & 0xff00)
+               return false;
+       return true;
+}
+
+static inline bool addr_valid8bit(u16 addr, u16 size)
+{
+       return is_8bit(addr) && is_8bit(size) &&
+               is_8bit(addr + size - 1);
+}
+/* Get address space size */
+static ssize_t get_ram_size(struct c2port_device *dev)
+{
+       return dev->ops->ram_size;
+}
+
+static ssize_t get_sfr_size(struct c2port_device *dev)
+{
+       return dev->ops->sfr_size;
+}
+
+static ssize_t get_xram_size(struct c2port_device *dev)
+{
+       return dev->ops->xram_size;
+}
+
+/* calculate how many bytes to read/write */
+static ssize_t calc_bytes(loff_t start, u16 mem_size, u16 expected_size)
+{
+       if (start > mem_size)
+               return 0;
+       if (start + expected_size > mem_size)
+               return mem_size - start;
+       return expected_size;
+}
+
+/* Send target address to C2 port, format: 1byte addr, 1byte size */
+static ssize_t send_addr_ram(struct c2port_device *dev, u16 addr, u16 size)
+{
+       int ret;
+
+       /* Overflow dectction */
+       if (!addr_valid8bit(addr, size)) {
+               dev_err(dev->dev, "Address/size overflow!\n");
+               return -EINVAL;
+       }
+
+       /* Write start address */
+       c2port_write_dr(dev, addr);
+       ret = c2port_poll_in_busy(dev);
+       if (ret < 0)
+               return ret;
+
+       /* Send address block size */
+       c2port_write_dr(dev, size);
+       ret = c2port_poll_in_busy(dev);
+       return ret;
+}
+/* Externl RAM address format: 2byte addr, size not used for XRAM */
+/* Address is increased automatically when write/read to XRAMD */
+static ssize_t send_addr_xram(struct c2port_device *dev, u16 addr, u16 size)
+{
+       int ret;
+
+       /* Write start address */
+       /* Low byte */
+       c2port_write_ar(dev, C2PORT_XRAMAL);
+       c2port_write_dr(dev, (u8) addr);
+       ret = c2port_poll_in_busy(dev);
+       if (ret < 0)
+               return ret;
+       /* High byte */
+       c2port_write_ar(dev, C2PORT_XRAMAH);
+       c2port_write_dr(dev, (u8) (addr >> 8));
+       ret = c2port_poll_in_busy(dev);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+/* Write FPDAT command, XRAM manipulation doesn't need it */
+static ssize_t write_command(struct c2port_device *c2dev, u8 command)
+{
+       int ret;
+       u8 status;
+
+       c2port_write_ar(c2dev, C2PORT_FPDAT);
+       c2port_write_dr(c2dev, command);
+
+       /* Wait for command status */
+       ret = c2port_poll_in_busy(c2dev);
+       if (ret < 0)
+               return ret;
+
+       /* Wait for status information */
+       ret = c2port_poll_out_ready(c2dev);
+       if (ret < 0)
+               return ret;
+
+       /* Read command status */
+       ret = c2port_read_dr(c2dev, &status);
+       if (ret < 0)
+               return ret;
+       if (status != C2PORT_COMMAND_OK)
+               return -EBUSY;
+       return 0;
+}
+/* Read bytes for SFR/internal memory */
+static ssize_t read_bytes_common(struct c2port_device *dev, char *buffer,
+                                                u16 nread)
+{
+       int i;
+       u8 ret;
+       /* Read flash block */
+       for (i = 0; i < nread; i++) {
+               ret = c2port_poll_out_ready(dev);
+               if (ret < 0)
+                       return ret;
+
+               ret = c2port_read_dr(dev, buffer + i);
+               if (ret < 0)
+                       return ret;
+       }
+       return nread;
+}
+
+static ssize_t write_bytes_common(struct c2port_device *dev, char *buffer,
+                                                       u16 nwrite)
+{
+       int i;
+       int ret;
+
+       for (i = 0; i < nwrite; i++) {
+               ret = c2port_write_dr(dev, *(buffer + i));
+               if (ret < 0)
+                       return ret;
+
+               ret = c2port_poll_in_busy(dev);
+               if (ret < 0)
+                       return ret;
+       }
+       return nwrite;
+}
+
+static ssize_t read_bytes_xram(struct c2port_device *dev, char *buffer,
+                                                       u16 nread)
+{
+       int i;
+       u8 ret;
+       /* Switch to read/write of XRAMD */
+       c2port_write_ar(dev, C2PORT_XRAMD);
+
+       for (i = 0; i < nread; i++) {
+               /* No need to poll out ready */
+               ret = c2port_read_dr(dev, buffer + i);
+               if (ret < 0)
+                       return ret;
+       }
+       return nread;
+}
+
+static ssize_t write_bytes_xram(struct c2port_device *dev, char *buffer,
+                                                       u16 nwrite)
+{
+       int i;
+       int ret;
+       /* Switch to read/write of XRAMD */
+       c2port_write_ar(dev, C2PORT_XRAMD);
+
+       for (i = 0; i < nwrite; i++) {
+               ret = c2port_write_dr(dev, *(buffer + i));
+               if (ret < 0)
+                       return ret;
+               /* No needn to poll in busy */
+       }
+       return nwrite;
+}
+
+/* desciption for every operation */
+struct c2port_internal_operation {
+       /* address access type */
+       ssize_t (*get_addr_size)(struct c2port_device *dev);
+       /* Which command to write to FPDAT register */
+       u8 command;
+       /* Send address and how many bytes/blocks to read/write */
+       ssize_t (*send_addr_and_count) (struct c2port_device *dev,
+                                       u16 start_addr, u16 count);
+       /* functions to execute read/write */
+       ssize_t (*operation) (struct c2port_device *dev, char *buffer,
+                               u16 count);
+};
+
+/* Operations for manipulating internal ram */
+static struct c2port_internal_operation read_iram_ops = {
+       .get_addr_size = get_ram_size,
+       .command = C2PORT_INDIRECT_READ,
+       .send_addr_and_count = send_addr_ram,
+       .operation = read_bytes_common
+};
+
+static struct c2port_internal_operation write_iram_ops = {
+       .get_addr_size = get_ram_size,
+       .command = C2PORT_INDIRECT_WRITE,
+       .send_addr_and_count = send_addr_ram,
+       .operation = write_bytes_common
+};
+
+/* Operations for manipulating special function registers */
+static struct c2port_internal_operation read_sfr_ops = {
+       .get_addr_size = get_sfr_size,
+       .command = C2PORT_DIRECT_READ,
+       .send_addr_and_count = send_addr_ram,
+       .operation = read_bytes_common
+};
+
+static struct c2port_internal_operation write_sfr_ops = {
+       .get_addr_size = get_sfr_size,
+       .command = C2PORT_DIRECT_WRITE,
+       .send_addr_and_count = send_addr_ram,
+       .operation = write_bytes_common
+};
+
+/* Operations for manipulating XRAM and USB FIFO */
+static struct c2port_internal_operation read_xram_ops = {
+       .get_addr_size = get_xram_size,
+       /* No FPDAT operation */
+       .command = 0,
+       .send_addr_and_count = send_addr_xram,
+       .operation = read_bytes_xram
+};
+
+static struct c2port_internal_operation write_xram_ops = {
+       .get_addr_size = get_xram_size,
+       /* No FPDAT operation */
+       .command = 0,
+       .send_addr_and_count = send_addr_xram,
+       .operation = write_bytes_xram
+};
+
+/* General framework manipulating RAM/SFR/XRAM */
+static ssize_t c2port_operation_execute(struct c2port_internal_operation *c2op,
+                                       struct file *filp, struct kobject *kobj,
+                                       char *buffer, loff_t offset,
+                                       size_t count)
+{
+       struct c2port_device *c2dev = dev_get_drvdata(container_of(kobj,
+                                               struct  device, kobj));
+       ssize_t ret;
+       u16 nreadwrite = (u16)count;
+
+       if (!c2dev->access || !mcu_halted(c2dev))
+               return -EBUSY;
+
+       mutex_lock(&c2dev->mutex);
+
+       nreadwrite = calc_bytes(offset, c2op->get_addr_size(c2dev), nreadwrite);
+       if (nreadwrite == 0) {
+               ret = 0;
+               goto out;
+       }
+       /* Send command to FPDAT */
+       if (c2op->command != 0) {
+               ret = write_command(c2dev, c2op->command);
+               if (ret < 0) {
+                       dev_err(c2dev->dev, "cannot write %s C2 command\n",
+                                       c2dev->name);
+                       goto out;
+               }
+       }
+
+       /* Send address and count */
+       ret = c2op->send_addr_and_count(c2dev, offset, nreadwrite);
+       if (ret < 0) {
+               dev_err(c2dev->dev, "cannot write %s C2 ram adress: 0x%02x\n",
+                               c2dev->name, (int)offset);
+               goto out;
+       }
+       /* do read/write */
+       ret = c2op->operation(c2dev, buffer, nreadwrite);
+       if (ret < 0)
+               dev_err(c2dev->dev, "cannot manipulate %s C2 ram data\n",
+                               c2dev->name);
+out:
+       mutex_unlock(&c2dev->mutex);
+       return ret;
+}
+
+static ssize_t c2port_read_internal_ram(struct file *filp, struct kobject *kobj,
+                                       struct bin_attribute *attr,
+                                       char *buffer, loff_t offset,
+                                       size_t count)
+{
+       return c2port_operation_execute(&read_iram_ops, filp, kobj, buffer,
+                                       offset, count);
+}
+
+static ssize_t c2port_write_internal_ram(struct file *filp,
+                                        struct kobject *kobj,
+                                        struct bin_attribute *attr,
+                                        char *buffer, loff_t offset,
+                                        size_t count)
+{
+       return c2port_operation_execute(&write_iram_ops, filp, kobj, buffer,
+                                       offset, count);
+}
+
+static ssize_t c2port_read_sfr(struct file *filp, struct kobject *kobj,
+                               struct bin_attribute *attr,
+                               char *buffer, loff_t offset, size_t count)
+{
+       return c2port_operation_execute(&read_sfr_ops, filp, kobj, buffer,
+                                       offset, count);
+}
+
+static ssize_t c2port_write_sfr(struct file *filp, struct kobject *kobj,
+                               struct bin_attribute *attr,
+                               char *buffer, loff_t offset, size_t count)
+{
+       return c2port_operation_execute(&write_sfr_ops, filp, kobj, buffer,
+                                       offset, count);
+}
+
+static ssize_t c2port_read_xram(struct file *filp, struct kobject *kobj,
+                               struct bin_attribute *attr,
+                               char *buffer, loff_t offset, size_t count)
+{
+       return c2port_operation_execute(&read_xram_ops, filp, kobj, buffer,
+                                       offset, count);
+}
+
+static ssize_t c2port_write_xram(struct file *filp, struct kobject *kobj,
+                                struct bin_attribute *attr,
+                                char *buffer, loff_t offset, size_t count)
+{
+       return c2port_operation_execute(&write_xram_ops, filp, kobj, buffer,
+                                       offset, count);
+}
+
 /*
  * Class attributes
  */
@@ -864,6 +1269,12 @@ static struct device_attribute c2port_attrs[] = {
        __ATTR(flash_access, 0644, c2port_show_flash_access,
                                        c2port_store_flash_access),
        __ATTR(flash_erase, 0200, NULL, c2port_store_flash_erase),
+       /* before read/write to ram/sfr/xram, should halt mcu */
+       __ATTR(ram_access, 0644, c2port_show_ram_access,
+                                       c2port_store_ram_access),
+       /* reset the device and then halt it */
+       __ATTR(reset_halt, 0200, NULL,
+                                               c2port_store_rest_halt_access),
        __ATTR_NULL,
 };
 
@@ -877,6 +1288,33 @@ static struct bin_attribute c2port_bin_attrs = {
        /* .size is computed at run-time */
 };
 
+/* Add supports for ram read/write */
+static struct bin_attribute c2port_bin_ram_attrs = {
+       .attr = {
+               .name = "internal_ram",
+               .mode = 0644},
+       .read = c2port_read_internal_ram,
+       .write = c2port_write_internal_ram
+};
+
+/* Add supports for sfr read/write */
+static struct bin_attribute c2port_bin_sfr_attrs = {
+       .attr = {
+                .name = "sfr",
+                .mode = 0644},
+       .read = c2port_read_sfr,
+       .write = c2port_write_sfr
+};
+
+/* Add supports for xram read/write */
+static struct bin_attribute c2port_bin_xram_attrs = {
+       .attr = {
+                .name = "xram",
+                .mode = 0644},
+       .read = c2port_read_xram,
+       .write = c2port_write_xram
+};
+
 /*
  * Exported functions
  */
@@ -925,6 +1363,31 @@ struct c2port_device *c2port_device_register(char *name,
        if (unlikely(ret))
                goto error_device_create_bin_file;
 
+       /* Create binary file for ram read/write */
+       if (ops->sfr_size) {
+               c2port_bin_ram_attrs.size = ops->ram_size;
+               ret = device_create_bin_file(c2dev->dev, &c2port_bin_ram_attrs);
+               if (unlikely(ret))
+                       goto error_device_create_bin_file;
+       }
+
+       /* Create binary file for sfr read/write */
+       if (ops->sfr_size) {
+               c2port_bin_sfr_attrs.size = ops->sfr_size;
+               ret = device_create_bin_file(c2dev->dev, &c2port_bin_sfr_attrs);
+               if (unlikely(ret))
+                       goto error_device_create_bin_file;
+       }
+
+       /* Create binary file for xram read/write */
+       if (ops->xram_size) {
+               c2port_bin_xram_attrs.size = ops->xram_size;
+               ret = device_create_bin_file(c2dev->dev,
+                               &c2port_bin_xram_attrs);
+               if (unlikely(ret))
+                       goto error_device_create_bin_file;
+       }
+
        /* By default C2 port access is off */
        c2dev->access = c2dev->flash_access = 0;
        ops->access(c2dev, 0);
index 4efabcb..8e4b35e 100644 (file)
@@ -3,6 +3,7 @@
  *
  *  Copyright (c) 2007 Rodolfo Giometti <giometti@linux.it>
  *  Copyright (c) 2007 Eurotech S.p.A. <info@eurotech.it>
+ *  Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published by
@@ -42,6 +43,12 @@ struct c2port_ops {
        /* Flash layout */
        unsigned short block_size;      /* flash block size in bytes */
        unsigned short blocks_num;      /* flash blocks number */
+       /* INTERNAL RAM layout */
+       unsigned short ram_size;
+       /* SFR layout */
+       unsigned short sfr_size;
+       /* XRAM layout */
+       unsigned short xram_size;
 
        /* Enable or disable the access to C2 port */
        void (*access)(struct c2port_device *dev, int status);
diff --git a/include/linux/platform_data/tegra_c2port_platform_data.h b/include/linux/platform_data/tegra_c2port_platform_data.h
new file mode 100644 (file)
index 0000000..c5adab1
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+ #ifndef __MACH_TEGRA_MCU_C2PORT_PLATFORM_DATA_H
+ #define __MACH_TEGRA_MCU_C2PORT_PLATFORM_DATA_H
+
+/* The platform data is used to tell c2port driver
+ * GPIO configuratioon.
+ */
+struct tegra_c2port_platform_data {
+       unsigned int gpio_c2ck;
+       unsigned int gpio_c2d;
+};
+
+ #endif
+