[PATCH 2/2] misc: c2c: Add C2C(Chip to Chip) device driver for EXYNOS

Kisang Lee kisang80.lee at samsung.com
Sat Feb 4 03:15:03 EST 2012


Cc: Arnd Bergmann <arnd <at> arndb.de>
Cc: Greg Kroah-Hartman <greg <at> kroah.com>

Signed-off-by: Kisang Lee <kisang80.lee at samsung.com>
---
 drivers/misc/Kconfig           |    1 +
 drivers/misc/Makefile          |    1 +
 drivers/misc/c2c/Kconfig       |   10 +
 drivers/misc/c2c/Makefile      |    5 +
 drivers/misc/c2c/samsung-c2c.c |  500 ++++++++++++++++++++++++++++++++++++++++
 drivers/misc/c2c/samsung-c2c.h |  300 ++++++++++++++++++++++++
 6 files changed, 817 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/c2c/Kconfig
 create mode 100644 drivers/misc/c2c/Makefile
 create mode 100644 drivers/misc/c2c/samsung-c2c.c
 create mode 100644 drivers/misc/c2c/samsung-c2c.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 6a1a092..2ec3f48 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -516,5 +516,6 @@ source "drivers/misc/ti-st/Kconfig"
 source "drivers/misc/lis3lv02d/Kconfig"
 source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
+source "drivers/misc/c2c/Kconfig"
 
 endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 3e1d801..bd96ed7 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -49,3 +49,4 @@ obj-y				+= carma/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
 obj-$(CONFIG_MAX8997_MUIC)	+= max8997-muic.o
+obj-$(CONFIG_SAMSUNG_C2C)	+= c2c/
diff --git a/drivers/misc/c2c/Kconfig b/drivers/misc/c2c/Kconfig
new file mode 100644
index 0000000..cd13078
--- /dev/null
+++ b/drivers/misc/c2c/Kconfig
@@ -0,0 +1,10 @@
+#
+# C2C(Chip to Chip) Device
+#
+
+config SAMSUNG_C2C
+	tristate "C2C Support"
+	depends on SOC_EXYNOS4212 || SOC_EXYNOS5250
+	default n
+	help
+	  It is for supporting C2C driver.
diff --git a/drivers/misc/c2c/Makefile b/drivers/misc/c2c/Makefile
new file mode 100644
index 0000000..1af7d3a
--- /dev/null
+++ b/drivers/misc/c2c/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for C2C(Chip to Chip) device
+#
+
+obj-$(CONFIG_SAMSUNG_C2C)	+= samsung-c2c.o
diff --git a/drivers/misc/c2c/samsung-c2c.c b/drivers/misc/c2c/samsung-c2c.c
new file mode 100644
index 0000000..ee08ac6
--- /dev/null
+++ b/drivers/misc/c2c/samsung-c2c.c
@@ -0,0 +1,500 @@
+/*
+ * Samsung C2C driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Kisang Lee <kisang80.lee at samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/sysfs.h>
+#include <asm/mach-types.h>
+
+#include <mach/c2c.h>
+#include <mach/regs-c2c.h>
+#include <plat/cpu.h>
+
+#include "samsung-c2c.h"
+
+void c2c_reset_ops(void)
+{
+	u32 set_clk = 0;
+
+	if (c2c_con.opp_mode == C2C_OPP100)
+		set_clk = c2c_con.clk_opp100;
+	else if (c2c_con.opp_mode == C2C_OPP50)
+		set_clk = c2c_con.clk_opp50;
+	else if (c2c_con.opp_mode == C2C_OPP25)
+		set_clk = c2c_con.clk_opp25;
+
+	dev_info(c2c_con.c2c_dev, "c2c_reset_ops()\n");
+	clk_set_rate(c2c_con.c2c_sclk, (set_clk + 1) * MHZ);
+	c2c_set_func_clk(set_clk);
+
+	/* C2C block reset */
+	c2c_set_reset(C2C_CLEAR);
+	c2c_set_reset(C2C_SET);
+
+	c2c_set_clock_gating(C2C_CLEAR);
+	c2c_writel(c2c_con.retention_reg, EXYNOS_C2C_IRQ_EN_SET1);
+	c2c_writel(set_clk, EXYNOS_C2C_FCLK_FREQ);
+	c2c_writel(set_clk, EXYNOS_C2C_RX_MAX_FREQ);
+	c2c_set_clock_gating(C2C_SET);
+}
+
+static ssize_t c2c_ctrl_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	int ret = 0;
+	ret = sprintf(buf, "C2C State");
+	ret += sprintf(&buf[ret], "SysReg : 0x%x\n",
+			readl(c2c_con.c2c_sysreg));
+	ret += sprintf(&buf[ret], "Port Config : 0x%x\n",
+			c2c_readl(EXYNOS_C2C_PORTCONFIG));
+	ret += sprintf(&buf[ret], "FCLK_FREQ : %d\n",
+			c2c_readl(EXYNOS_C2C_FCLK_FREQ));
+	ret += sprintf(&buf[ret], "RX_MAX_FREQ : %d\n",
+			c2c_readl(EXYNOS_C2C_RX_MAX_FREQ));
+	ret += sprintf(&buf[ret], "IRQ_EN_SET1 : 0x%x\n",
+			c2c_readl(EXYNOS_C2C_IRQ_EN_SET1));
+	ret += sprintf(&buf[ret], "Get C2C sclk rate : %ld\n",
+			clk_get_rate(c2c_con.c2c_sclk));
+	ret += sprintf(&buf[ret], "Get C2C aclk rate : %ld\n",
+			clk_get_rate(c2c_con.c2c_aclk));
+
+	return ret;
+}
+
+static ssize_t c2c_ctrl_store(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	int ops_num, opp_val, req_clk;
+	sscanf(buf, "%d", &ops_num);
+
+	switch (ops_num) {
+	case 1:
+		c2c_reset_ops();
+		break;
+	case 2:
+	case 3:
+	case 4:
+		opp_val = ops_num - 1;
+		req_clk = 0;
+		dev_info(c2c_con.c2c_dev, "Set current OPP mode (%d)\n",
+			opp_val);
+
+		if (opp_val == C2C_OPP100)
+			req_clk = c2c_con.clk_opp100;
+		else if (opp_val == C2C_OPP50)
+			req_clk = c2c_con.clk_opp50;
+		else if (opp_val == C2C_OPP25)
+			req_clk = c2c_con.clk_opp25;
+
+		if (opp_val == 0 || req_clk == 1) {
+			dev_info(c2c_con.c2c_dev,
+				"This mode is not reserved in OPP mode.\n");
+		} else {
+			c2c_set_clock_gating(C2C_CLEAR);
+			if (c2c_con.opp_mode > opp_val) {
+				/* increase case */
+				clk_set_rate(c2c_con.c2c_sclk,
+					(req_clk + 1) * MHZ);
+				c2c_writel(req_clk, EXYNOS_C2C_FCLK_FREQ);
+				c2c_set_func_clk(req_clk);
+				c2c_writel(req_clk, EXYNOS_C2C_RX_MAX_FREQ);
+			} else if (c2c_con.opp_mode < opp_val) {
+				/* decrease case */
+				c2c_writel(req_clk, EXYNOS_C2C_RX_MAX_FREQ);
+				clk_set_rate(c2c_con.c2c_sclk,
+					(req_clk + 1) * MHZ);
+				c2c_writel(req_clk, EXYNOS_C2C_FCLK_FREQ);
+				c2c_set_func_clk(req_clk);
+			} else{
+				dev_info(c2c_con.c2c_dev,
+					"Requested same OPP mode\n");
+			}
+			c2c_con.opp_mode = opp_val;
+			c2c_set_clock_gating(C2C_SET);
+		}
+
+		dev_info(c2c_con.c2c_dev, "Get C2C sclk rate : %ld\n",
+					clk_get_rate(c2c_con.c2c_sclk));
+		dev_info(c2c_con.c2c_dev, "Get C2C aclk rate : %ld\n",
+					clk_get_rate(c2c_con.c2c_aclk));
+		break;
+	default:
+		dev_info(c2c_con.c2c_dev, "---C2C Operation Number---\n");
+		dev_info(c2c_con.c2c_dev, "1. C2C Reset\n");
+		dev_info(c2c_con.c2c_dev, "2. Set OPP25\n");
+		dev_info(c2c_con.c2c_dev, "3. Set OPP50\n");
+		dev_info(c2c_con.c2c_dev, "4. Set OPP100\n");
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(c2c_ctrl, 0644, c2c_ctrl_show, c2c_ctrl_store);
+
+static int c2c_set_sharedmem(enum c2c_shrdmem_size size, u32 addr)
+{
+	dev_info(c2c_con.c2c_dev, "Set BaseAddr(0x%x) and Size(%d)\n",
+				addr, 1 << (2 + size));
+
+	/* Set DRAM Base Addr & Size */
+	c2c_set_shdmem_size(size);
+	c2c_set_base_addr((addr >> 22));
+
+	return 0;
+}
+
+static void c2c_set_interrupt(u32 genio_num, enum c2c_interrupt set_int)
+{
+	u32 cur_int_reg, cur_lev_reg;
+
+	cur_int_reg = c2c_readl(EXYNOS_C2C_GENO_INT);
+	cur_lev_reg = c2c_readl(EXYNOS_C2C_GENO_LEVEL);
+
+	switch (set_int) {
+	case C2C_INT_TOGGLE:
+		cur_int_reg &= ~(0x1 << genio_num);
+		c2c_writel(cur_int_reg, EXYNOS_C2C_GENO_INT);
+		break;
+	case C2C_INT_HIGH:
+		cur_int_reg |= (0x1 << genio_num);
+		cur_lev_reg |= (0x1 << genio_num);
+		c2c_writel(cur_int_reg, EXYNOS_C2C_GENO_INT);
+		c2c_writel(cur_lev_reg, EXYNOS_C2C_GENO_LEVEL);
+		break;
+	case C2C_INT_LOW:
+		cur_int_reg |= (0x1 << genio_num);
+		cur_lev_reg &= ~(0x1 << genio_num);
+		c2c_writel(cur_int_reg, EXYNOS_C2C_GENO_INT);
+		c2c_writel(cur_lev_reg, EXYNOS_C2C_GENO_LEVEL);
+		break;
+	}
+}
+
+static irqreturn_t c2c_sscm0_irq(int irq, void *data)
+{
+	/* Nothing here yet */
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t c2c_sscm1_irq(int irq, void *data)
+{
+	u32 raw_irq, opp_val, req_clk;
+	raw_irq = c2c_readl(EXYNOS_C2C_IRQ_EN_STAT1);
+
+	if ((raw_irq >> C2C_GENIO_OPP_INT) & 1) { /* OPP Change */
+		/*
+		 * OPP mode GENI/O bit definition[29:27]
+		 * OPP100 GENI/O[29:28] : 1 1
+		 * OPP50 GENI/O[29:28] : 1 0
+		 * OPP25 GENI/O[29:28] : 0 1
+		 * GENI[27] is only used for making interrupt.
+		*/
+		opp_val = (c2c_readl(EXYNOS_C2C_GENO_STATUS) >> 28) & 3;
+		req_clk = 0;
+		dev_info(c2c_con.c2c_dev, "OPP interrupt occured (%d)\n",
+				opp_val);
+
+		if (opp_val == C2C_OPP100)
+			req_clk = c2c_con.clk_opp100;
+		else if (opp_val == C2C_OPP50)
+			req_clk = c2c_con.clk_opp50;
+		else if (opp_val == C2C_OPP25)
+			req_clk = c2c_con.clk_opp25;
+
+		if (opp_val == 0 || req_clk == 1) {
+			dev_info(c2c_con.c2c_dev,
+				"This mode is not reserved in OPP mode.\n");
+		} else {
+			if (c2c_con.opp_mode > opp_val) {
+				/* increase case */
+				clk_set_rate(c2c_con.c2c_sclk,
+					(req_clk + 1) * MHZ);
+				c2c_writel(req_clk, EXYNOS_C2C_FCLK_FREQ);
+				c2c_set_func_clk(req_clk);
+				c2c_writel(req_clk, EXYNOS_C2C_RX_MAX_FREQ);
+			} else if (c2c_con.opp_mode < opp_val) {
+				/* decrease case */
+				c2c_writel(req_clk, EXYNOS_C2C_RX_MAX_FREQ);
+				clk_set_rate(c2c_con.c2c_sclk,
+					(req_clk + 1) * MHZ);
+				c2c_writel(req_clk, EXYNOS_C2C_FCLK_FREQ);
+				c2c_set_func_clk(req_clk);
+			} else{
+				dev_info(c2c_con.c2c_dev, "Requested same OPP mode\n");
+			}
+			c2c_con.opp_mode = opp_val;
+		}
+
+		/* Interrupt Clear */
+		c2c_writel((0x1 << C2C_GENIO_OPP_INT), EXYNOS_C2C_IRQ_EN_STAT1);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void set_c2c_device(struct platform_device *pdev)
+{
+	struct exynos_c2c_platdata *pdata = pdev->dev.platform_data;
+	u32 default_clk;
+
+	c2c_con.c2c_sysreg = pdata->c2c_sysreg;
+	c2c_con.rx_width = pdata->rx_width;
+	c2c_con.tx_width = pdata->tx_width;
+	c2c_con.clk_opp100 = pdata->clk_opp100;
+	c2c_con.clk_opp50 = pdata->clk_opp50;
+	c2c_con.clk_opp25 = pdata->clk_opp25;
+	c2c_con.opp_mode = pdata->default_opp_mode;
+	c2c_con.c2c_sclk = clk_get(&pdev->dev, "sclk_c2c");
+	c2c_con.c2c_aclk = clk_get(&pdev->dev, "aclk_c2c");
+
+	/* Set clock to default mode */
+	if (c2c_con.opp_mode == C2C_OPP100)
+		default_clk = c2c_con.clk_opp100;
+	else if (c2c_con.opp_mode == C2C_OPP50)
+		default_clk = c2c_con.clk_opp50;
+	else if (c2c_con.opp_mode == C2C_OPP25)
+		default_clk = c2c_con.clk_opp25;
+	else {
+		dev_info(c2c_con.c2c_dev, "Default OPP mode is not selected.\n");
+		c2c_con.opp_mode = C2C_OPP50;
+		default_clk = c2c_con.clk_opp50;
+	}
+
+	clk_set_rate(c2c_con.c2c_sclk, (default_clk + 1)  * MHZ);
+	clk_set_rate(c2c_con.c2c_aclk, ((default_clk / 2) + 1) * MHZ);
+
+	dev_info(c2c_con.c2c_dev, "Get C2C sclk rate : %ld\n",
+				clk_get_rate(c2c_con.c2c_sclk));
+	dev_info(c2c_con.c2c_dev, "Get C2C aclk rate : %ld\n",
+				clk_get_rate(c2c_con.c2c_aclk));
+
+	if (pdata->setup_gpio)
+		pdata->setup_gpio(pdata->rx_width, pdata->tx_width);
+
+	c2c_set_sharedmem(pdata->shdmem_size, pdata->shdmem_addr);
+
+	/* Set SYSREG to memdone */
+	c2c_set_memdone(C2C_SET);
+	c2c_set_clock_gating(C2C_CLEAR);
+
+	/* Set C2C clock register */
+	c2c_writel(default_clk, EXYNOS_C2C_FCLK_FREQ);
+	c2c_writel(default_clk, EXYNOS_C2C_RX_MAX_FREQ);
+	c2c_set_func_clk(default_clk);
+
+	/* Set C2C buswidth */
+	c2c_writel(((pdata->rx_width << 4) | (pdata->tx_width)),
+			EXYNOS_C2C_PORTCONFIG);
+	c2c_set_tx_buswidth(pdata->tx_width);
+	c2c_set_rx_buswidth(pdata->rx_width);
+
+	/* Enable defined GENI/O Interrupt */
+	c2c_writel((0x1 << C2C_GENIO_OPP_INT), EXYNOS_C2C_IRQ_EN_SET1);
+	c2c_con.retention_reg = (0x1 << C2C_GENIO_OPP_INT);
+
+	c2c_set_interrupt(C2C_GENIO_OPP_INT, C2C_INT_HIGH);
+
+	dev_info(c2c_con.c2c_dev, "Port Config : 0x%x\n",
+			c2c_readl(EXYNOS_C2C_PORTCONFIG));
+	dev_info(c2c_con.c2c_dev, "FCLK_FREQ register : %d\n",
+			c2c_readl(EXYNOS_C2C_FCLK_FREQ));
+	dev_info(c2c_con.c2c_dev, "RX_MAX_FREQ register : %d\n",
+			c2c_readl(EXYNOS_C2C_RX_MAX_FREQ));
+	dev_info(c2c_con.c2c_dev, "IRQ_EN_SET1 register : 0x%x\n",
+			c2c_readl(EXYNOS_C2C_IRQ_EN_SET1));
+
+	c2c_set_clock_gating(C2C_SET);
+}
+
+static int __devinit samsung_c2c_probe(struct platform_device *pdev)
+{
+	struct exynos_c2c_platdata *pdata = pdev->dev.platform_data;
+	struct resource *res = NULL;
+	struct resource *res1 = NULL;
+	int sscm_irq0, sscm_irq1;
+	int err = 0;
+
+	c2c_con.c2c_dev = &pdev->dev;
+
+	/* resource for AP's SSCM region */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource defined(AP's SSCM)\n");
+		return -ENOENT;
+	}
+	res = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (!res) {
+		dev_err(&pdev->dev, "failded to request memory resource(AP)\n");
+		return -ENOENT;
+	}
+	pdata->ap_sscm_addr = ioremap(res->start, resource_size(res));
+	if (!pdata->ap_sscm_addr) {
+		dev_err(&pdev->dev, "failded to request memory resource(AP)\n");
+		goto release_ap_sscm;
+	}
+	c2c_con.ap_sscm_addr = pdata->ap_sscm_addr;
+
+	/* resource for CP's SSCM region */
+	res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res1) {
+		dev_err(&pdev->dev, "no memory resource defined(AP's SSCM)\n");
+		goto unmap_ap_sscm;
+	}
+	res1 = request_mem_region(res1->start, resource_size(res1), pdev->name);
+	if (!res1) {
+		dev_err(&pdev->dev, "failded to request memory resource(AP)\n");
+		goto unmap_ap_sscm;
+	}
+	pdata->cp_sscm_addr = ioremap(res1->start, resource_size(res1));
+	if (!pdata->cp_sscm_addr) {
+		dev_err(&pdev->dev, "failded to request memory resource(CP)\n");
+		goto release_cp_sscm;
+	}
+	c2c_con.cp_sscm_addr = pdata->cp_sscm_addr;
+
+	/* Request IRQ for SSCM0 */
+	sscm_irq0 = platform_get_irq(pdev, 0);
+	if (sscm_irq0 < 0) {
+		dev_err(&pdev->dev, "no irq specified\n");
+		goto unmap_cp_sscm;
+	}
+	err = request_irq(sscm_irq0, c2c_sscm0_irq, 0, pdev->name, pdev);
+	if (err) {
+		dev_err(&pdev->dev, "Can't request SSCM0 IRQ\n");
+		goto unmap_cp_sscm;
+	}
+	/* SSCM0 irq will be only used for master device */
+	disable_irq(sscm_irq0);
+
+	/* Request IRQ for SSCM1 */
+	sscm_irq1 = platform_get_irq(pdev, 1);
+	if (sscm_irq1 < 0) {
+		dev_err(&pdev->dev, "no irq specified\n");
+		goto release_sscm_irq0;
+	}
+	err = request_irq(sscm_irq1, c2c_sscm1_irq, 1, pdev->name, pdev);
+	if (err) {
+		dev_err(&pdev->dev, "Can't request SSCM1 IRQ\n");
+		goto release_sscm_irq0;
+	}
+
+	if (err) {
+		dev_err(&pdev->dev, "Can't register chrdev!\n");
+		goto release_sscm_irq0;
+	}
+
+	set_c2c_device(pdev);
+
+	/* Create sysfs file for C2C debug */
+	err = device_create_file(&pdev->dev, &dev_attr_c2c_ctrl);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to create sysfs for C2C\n");
+		goto release_sscm_irq1;
+	}
+
+	return 0;
+
+release_sscm_irq1:
+	free_irq(sscm_irq1, pdev);
+
+release_sscm_irq0:
+	free_irq(sscm_irq0, pdev);
+
+unmap_cp_sscm:
+	iounmap(pdata->cp_sscm_addr);
+
+release_cp_sscm:
+	release_mem_region(res1->start, resource_size(res1));
+
+unmap_ap_sscm:
+	iounmap(pdata->ap_sscm_addr);
+
+release_ap_sscm:
+	release_mem_region(res->start, resource_size(res));
+
+	return err;
+}
+
+static int __devexit samsung_c2c_remove(struct platform_device *pdev)
+{
+	struct exynos_c2c_platdata *pdata = pdev->dev.platform_data;
+	struct resource *res = NULL;
+	struct resource *res1 = NULL;
+	int sscm_irq0, sscm_irq1;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	sscm_irq0 = platform_get_irq(pdev, 0);
+	sscm_irq1 = platform_get_irq(pdev, 1);
+
+	iounmap(pdata->ap_sscm_addr);
+	iounmap(pdata->cp_sscm_addr);
+	release_mem_region(res->start, resource_size(res));
+	release_mem_region(res1->start, resource_size(res1));
+
+	free_irq(sscm_irq0, pdev);
+	free_irq(sscm_irq1, pdev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int samsung_c2c_suspend(struct platform_device *dev, pm_message_t pm)
+{
+	/* Nothing here yet */
+	return 0;
+}
+
+static int samsung_c2c_resume(struct platform_device *dev)
+{
+	/* Nothing here yet */
+	return 0;
+}
+#else
+#define samsung_c2c_suspend NULL
+#define samsung_c2c_resume NULL
+#endif
+
+static struct platform_driver samsung_c2c_driver = {
+	.probe		= samsung_c2c_probe,
+	.remove		= __devexit_p(samsung_c2c_remove),
+	.suspend	= samsung_c2c_suspend,
+	.resume		= samsung_c2c_resume,
+	.driver		= {
+		.name	= "samsung-c2c",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init samsung_c2c_init(void)
+{
+	return platform_driver_register(&samsung_c2c_driver);
+}
+module_init(samsung_c2c_init);
+
+static void __exit samsung_c2c_exit(void)
+{
+	platform_driver_unregister(&samsung_c2c_driver);
+}
+module_exit(samsung_c2c_exit);
+
+MODULE_DESCRIPTION("Samsung C2C driver");
+MODULE_AUTHOR("Kisang Lee <kisang80.lee at samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/c2c/samsung-c2c.h b/drivers/misc/c2c/samsung-c2c.h
new file mode 100644
index 0000000..3f72137
--- /dev/null
+++ b/drivers/misc/c2c/samsung-c2c.h
@@ -0,0 +1,300 @@
+/*
+ * Samsung C2C driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Kisang Lee <kisang80.lee at samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#ifndef SAMSUNG_C2C_H
+#define SAMSUNG_C2C_H
+
+enum c2c_set_clear {
+	C2C_CLEAR = 0,
+	C2C_SET = 1,
+};
+
+enum c2c_interrupt {
+	C2C_INT_TOGGLE = 0,
+	C2C_INT_HIGH = 1,
+	C2C_INT_LOW = 2,
+};
+
+struct c2c_state_control {
+	void __iomem *ap_sscm_addr;
+	void __iomem *cp_sscm_addr;
+	struct device *c2c_dev;
+
+	u32 rx_width;
+	u32 tx_width;
+
+	u32 clk_opp100;
+	u32 clk_opp50;
+	u32 clk_opp25;
+
+	struct clk *c2c_sclk;
+	struct clk *c2c_aclk;
+
+	enum c2c_opp_mode opp_mode;
+
+	u32 retention_reg;
+	void __iomem *c2c_sysreg;
+};
+
+static struct c2c_state_control c2c_con;
+
+static inline void c2c_writel(u32 val, int reg)
+{
+	writel(val, c2c_con.ap_sscm_addr + reg);
+}
+
+static inline void c2c_writew(u16 val, int reg)
+{
+	writew(val, c2c_con.ap_sscm_addr + reg);
+}
+
+static inline void c2c_writeb(u8 val, int reg)
+{
+	writeb(val, c2c_con.ap_sscm_addr + reg);
+}
+
+static inline u32 c2c_readl(int reg)
+{
+	return readl(c2c_con.ap_sscm_addr + reg);
+}
+
+static inline u16 c2c_readw(int reg)
+{
+	return readw(c2c_con.ap_sscm_addr + reg);
+}
+
+static inline u8 c2c_readb(int reg)
+{
+	return readb(c2c_con.ap_sscm_addr + reg);
+}
+
+static inline void c2c_writel_cp(u32 val, int reg)
+{
+	writel(val, c2c_con.cp_sscm_addr + reg);
+}
+
+static inline void c2c_writew_cp(u16 val, int reg)
+{
+	writew(val, c2c_con.cp_sscm_addr + reg);
+}
+
+static inline void c2c_writeb_cp(u8 val, int reg)
+{
+	writeb(val, c2c_con.cp_sscm_addr + reg);
+}
+
+static inline u32 c2c_readl_cp(int reg)
+{
+	return readl(c2c_con.cp_sscm_addr + reg);
+}
+
+static inline u16 c2c_readw_cp(int reg)
+{
+	return readw(c2c_con.cp_sscm_addr + reg);
+}
+
+static inline u8 c2c_readb_cp(int reg)
+{
+	return readb(c2c_con.cp_sscm_addr + reg);
+}
+
+static inline enum c2c_set_clear c2c_get_clock_gating(void)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+	if (sysreg & (1 << C2C_SYSREG_CG))
+		return C2C_SET;
+	else
+		return C2C_CLEAR;
+}
+
+static inline void c2c_set_clock_gating(enum c2c_set_clear val)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	if (val == C2C_SET)
+		sysreg |= (1 << C2C_SYSREG_CG);
+	else
+		sysreg &= ~(1 << C2C_SYSREG_CG);
+
+	writel(sysreg, c2c_con.c2c_sysreg);
+}
+
+static inline enum c2c_set_clear c2c_get_memdone(void)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+	if (sysreg & (1 << C2C_SYSREG_MD))
+		return C2C_SET;
+	else
+		return C2C_CLEAR;
+}
+
+static inline void c2c_set_memdone(enum c2c_set_clear val)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	if (val == C2C_SET)
+		sysreg |= (1 << C2C_SYSREG_MD);
+	else
+		sysreg &= ~(1 << C2C_SYSREG_MD);
+
+	writel(sysreg, c2c_con.c2c_sysreg);
+}
+
+static inline enum c2c_set_clear c2c_get_master_on(void)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+	if (sysreg & (1 << C2C_SYSREG_MO))
+		return C2C_SET;
+	else
+		return C2C_CLEAR;
+}
+
+static inline void c2c_set_master_on(enum c2c_set_clear val)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	if (val == C2C_SET)
+		sysreg |= (1 << C2C_SYSREG_MO);
+	else
+		sysreg &= ~(1 << C2C_SYSREG_MO);
+
+	writel(sysreg, c2c_con.c2c_sysreg);
+}
+
+static inline u32 c2c_get_func_clk(void)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	sysreg &= (0x3ff << C2C_SYSREG_FCLK);
+
+	return sysreg >> C2C_SYSREG_FCLK;
+}
+
+static inline void c2c_set_func_clk(u32 val)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	sysreg &= ~(0x3ff << C2C_SYSREG_FCLK);
+	sysreg |= (val << C2C_SYSREG_FCLK);
+
+	writel(sysreg, c2c_con.c2c_sysreg);
+}
+
+static inline u32 c2c_get_tx_buswidth(void)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	sysreg &= (0x3 << C2C_SYSREG_TXW);
+
+	return sysreg >> C2C_SYSREG_TXW;
+}
+
+static inline void c2c_set_tx_buswidth(u32 val)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	sysreg &= ~(0x3 << C2C_SYSREG_TXW);
+	sysreg |= (val << C2C_SYSREG_TXW);
+
+	writel(sysreg, c2c_con.c2c_sysreg);
+}
+
+static inline u32 c2c_get_rx_buswidth(void)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	sysreg &= (0x3 << C2C_SYSREG_RXW);
+
+	return sysreg >> C2C_SYSREG_RXW;
+}
+
+static inline void c2c_set_rx_buswidth(u32 val)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	sysreg &= ~(0x3 << C2C_SYSREG_RXW);
+	sysreg |= (val << C2C_SYSREG_RXW);
+
+	writel(sysreg, c2c_con.c2c_sysreg);
+}
+
+static inline enum c2c_set_clear c2c_get_reset(void)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+	if (sysreg & (1 << C2C_SYSREG_RST))
+		return C2C_SET;
+	else
+		return C2C_CLEAR;
+}
+
+static inline void c2c_set_reset(enum c2c_set_clear val)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	if (val == C2C_SET)
+		sysreg |= (1 << C2C_SYSREG_RST);
+	else
+		sysreg &= ~(1 << C2C_SYSREG_RST);
+
+	writel(sysreg, c2c_con.c2c_sysreg);
+}
+
+static inline void c2c_set_rtrst(enum c2c_set_clear val)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	if (val == C2C_SET)
+		sysreg |= (1 << C2C_SYSREG_RTRST);
+	else
+		sysreg &= ~(1 << C2C_SYSREG_RTRST);
+
+	writel(sysreg, c2c_con.c2c_sysreg);
+}
+
+static inline u32 c2c_get_base_addr(void)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	sysreg &= (0x3ff << C2C_SYSREG_BASE_ADDR);
+
+	return sysreg >> C2C_SYSREG_BASE_ADDR;
+}
+
+static inline void c2c_set_base_addr(u32 val)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	sysreg &= ~(0x3ff << C2C_SYSREG_BASE_ADDR);
+	sysreg |= (val << C2C_SYSREG_BASE_ADDR);
+
+	writel(sysreg, c2c_con.c2c_sysreg);
+}
+
+static inline u32 c2c_get_shdmem_size(void)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	sysreg &= (0x7 << C2C_SYSREG_DRAM_SIZE);
+
+	return sysreg >> C2C_SYSREG_DRAM_SIZE;
+}
+
+static inline void c2c_set_shdmem_size(u32 val)
+{
+	u32 sysreg = readl(c2c_con.c2c_sysreg);
+
+	sysreg &= ~(0x7 << C2C_SYSREG_DRAM_SIZE);
+	sysreg |= (val << C2C_SYSREG_DRAM_SIZE);
+
+	writel(sysreg, c2c_con.c2c_sysreg);
+}
+
+#endif
-- 
1.7.1





More information about the linux-arm-kernel mailing list