[PATCH 2/2] misc: c2c: Add C2C(Chip to Chip) device driver for EXYNOS
Russell King - ARM Linux
linux at arm.linux.org.uk
Sat Feb 4 09:02:56 EST 2012
On Sat, Feb 04, 2012 at 05:15:03PM +0900, Kisang Lee wrote:
> 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>
What follows is a quick review of this driver. I think there's quite
a number of issues which need to be fixed before this driver is ready,
some of them are rather serious. Please take a look and try to address
as many of these comments as possible.
Thanks.
> ---
> 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>
I can find nothing related to the input layer in this file - is this
header required?
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/miscdevice.h>
I can find nothing which declares a misc_device in this file - is this
header required?
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/sysfs.h>
> +#include <asm/mach-types.h>
You appear to use nothing from this header, please remove it. Drivers
should not depend on the type of platform they're running on.
> +
> +#include <mach/c2c.h>
> +#include <mach/regs-c2c.h>
> +#include <plat/cpu.h>
Is plat/cpu.h necessary?
> +
> +#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);
What if sscanf fails?
> +
> + 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)
> +{
Is this function ever called with anything other than C2C_INT_HIGH ?
> + 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;
> +}
So is there any reason to even request this interrupt, potentially causing
an interrupt storm if it's triggered? By returning IRQ_HANDLED you're
telling the IRQ core that you serviced and cleared it, so if it gets
triggered, you're going to get stuck here.
> +
> +static irqreturn_t c2c_sscm1_irq(int irq, void *data)
> +{
If you passed &c2c_con into request_irq(), fixed c2c_readl etc to take a
c2c_con pointer, then you don't need to keep dereferencing a global symbol.
Note that the structure does nothing to optimize the generated code - the
compiler _won't_ keep the base address of it in a register and use offsets.
It'll just load the individual addresses to structure members.
If you want efficient code, then you have to use a pointer to the structure.
> + 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");
Where's the error checking? Nowhere in this file do you prepare and
enable the clock, so I assume that these can be deleted because they
don't ever actually supply any clock to the device at all.
> +
> + /* 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);
Driver name? The parent resource already contains the device 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) {
This is silly. Please name 'pdata' a const and fix the build warnings
that fall out from that. You really shouldn't be writing to random
data that you - as a driver - don't own. And as you seem to have
c2c_con.ap_sscm_addr to store it in, I see no reason for this to even
be happening.
> + 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);
Driver name? The parent resource already contains the device 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) {
Same comments as for pdata->ap_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;
> + }
Where do you remove this device file? It looks like an oops waiting to
happen if you remove this module.
> +
> + 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);
Why not use c2c_con.cp_sscm_addr ? Why not put &c2c_con into the
device driver data in the probe, and retrieve it at remove and
suspend/resume time?
> + 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;
> +}
If there's nothing that you want to do here, don't provide these
functions until you've thought of something to do. It just adds
unnecessary patch noise.
> +#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,
These two suspend/resume methods are superseded by the dev_pm_ops stuff.
Please convert to use this instead.
> + .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;
Why is this in a header file - and why do you only allow one of these
devices in the driver (with no protection against a second one being
registered.)
I see no reason why this file exists anyway - why isn't this in
drivers/misc/c2c/samsung-c2c.c ?
> +
> +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
>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
More information about the linux-arm-kernel
mailing list