Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- From c588a38519e40284de9aa980131930236e14b07c Mon Sep 17 00:00:00 2001
- From: Evan J Brunner <ej3@appitto.me>
- Date: Fri, 31 Oct 2014 11:05:39 -0700
- Subject: [PATCH] c2port: remove duramar2150, add GPIO
- CHNGD:c2port-core had somehow came to be
- called simply 'core' which was an
- ambigious name for a module, so name
- was reverted
- CHNGD:duramar client was not relevant, replaced
- with GENERIC_GPIO client
- ---
- drivers/misc/Makefile | 2 +-
- drivers/misc/c2port/Kconfig | 19 +-
- drivers/misc/c2port/Makefile | 5 +-
- drivers/misc/c2port/c2port-core.c | 1009 ++++++++++++++++++++++++++++++
- drivers/misc/c2port/c2port-duramar2150.c | 159 -----
- drivers/misc/c2port/c2port-gpio.c | 125 ++++
- drivers/misc/c2port/core.c | 1009 ------------------------------
- 7 files changed, 1146 insertions(+), 1182 deletions(-)
- create mode 100644 drivers/misc/c2port/c2port-core.c
- delete mode 100644 drivers/misc/c2port/c2port-duramar2150.c
- create mode 100644 drivers/misc/c2port/c2port-gpio.c
- delete mode 100644 drivers/misc/c2port/core.c
- diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
- index 24b40c3..5e5c23c 100644
- --- a/drivers/misc/Makefile
- +++ b/drivers/misc/Makefile
- @@ -36,7 +36,7 @@ obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
- obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
- obj-$(CONFIG_DS1682) += ds1682.o
- obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o
- -obj-$(CONFIG_C2PORT) += c2port/
- +obj-$(CONFIG_C2PORT_CORE) += c2port/
- obj-$(CONFIG_HMC6352) += hmc6352.o
- obj-y += eeprom/
- obj-y += cb710/
- diff --git a/drivers/misc/c2port/Kconfig b/drivers/misc/c2port/Kconfig
- index 0dd690e..b86bf6d 100644
- --- a/drivers/misc/c2port/Kconfig
- +++ b/drivers/misc/c2port/Kconfig
- @@ -2,7 +2,8 @@
- # C2 port devices
- #
- -menuconfig C2PORT
- +menu C2PORT
- +config C2PORT_CORE
- tristate "Silicon Labs C2 port support"
- default n
- help
- @@ -18,17 +19,15 @@ menuconfig C2PORT
- If you are not sure, say N here.
- -if C2PORT
- -config C2PORT_DURAMAR_2150
- - tristate "C2 port support for Eurotech's Duramar 2150"
- - depends on X86
- +config C2PORT_GPIO
- + tristate "C2 port support generic GPIO"
- + depends on GPIO_SYSFS
- default n
- help
- - This option enables C2 support for the Eurotech's Duramar 2150
- - on board micro controller.
- + This option enables C2 support for the c2port on a generic
- + gpio interface, using 'bit-banging'.
- To compile this driver as a module, choose M here: the module will
- - be called c2port-duramar2150.
- -
- -endif # C2PORT
- + be called c2port-gpio.
- +endmenu
- diff --git a/drivers/misc/c2port/Makefile b/drivers/misc/c2port/Makefile
- index 3b2cf43..77d90bd 100644
- --- a/drivers/misc/c2port/Makefile
- +++ b/drivers/misc/c2port/Makefile
- @@ -1,3 +1,2 @@
- -obj-$(CONFIG_C2PORT) += core.o
- -
- -obj-$(CONFIG_C2PORT_DURAMAR_2150) += c2port-duramar2150.o
- +obj-$(CONFIG_C2PORT_CORE) += c2port-core.o
- +obj-${CONFIG_C2PORT_GPIO} += c2port-gpio.o
- diff --git a/drivers/misc/c2port/c2port-core.c b/drivers/misc/c2port/c2port-core.c
- new file mode 100644
- index 0000000..464419b
- --- /dev/null
- +++ b/drivers/misc/c2port/c2port-core.c
- @@ -0,0 +1,1009 @@
- +/*
- + * Silicon Labs C2 port core Linux support
- + *
- + * Copyright (c) 2007 Rodolfo Giometti <giometti@linux.it>
- + * Copyright (c) 2007 Eurotech S.p.A. <info@eurotech.it>
- + *
- + * 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
- + * the Free Software Foundation
- + */
- +
- +#include <linux/module.h>
- +#include <linux/init.h>
- +#include <linux/device.h>
- +#include <linux/errno.h>
- +#include <linux/err.h>
- +#include <linux/kernel.h>
- +#include <linux/kmemcheck.h>
- +#include <linux/ctype.h>
- +#include <linux/delay.h>
- +#include <linux/idr.h>
- +#include <linux/sched.h>
- +#include <linux/slab.h>
- +
- +#include <linux/c2port.h>
- +
- +#define DRIVER_NAME "c2port"
- +#define DRIVER_VERSION "0.51.0"
- +
- +static DEFINE_SPINLOCK(c2port_idr_lock);
- +static DEFINE_IDR(c2port_idr);
- +
- +/*
- + * Local variables
- + */
- +
- +static struct class *c2port_class;
- +
- +/*
- + * C2 registers & commands defines
- + */
- +
- +/* C2 registers */
- +#define C2PORT_DEVICEID 0x00
- +#define C2PORT_REVID 0x01
- +#define C2PORT_FPCTL 0x02
- +#define C2PORT_FPDAT 0xB4
- +
- +/* C2 interface commands */
- +#define C2PORT_GET_VERSION 0x01
- +#define C2PORT_DEVICE_ERASE 0x03
- +#define C2PORT_BLOCK_READ 0x06
- +#define C2PORT_BLOCK_WRITE 0x07
- +#define C2PORT_PAGE_ERASE 0x08
- +
- +/* C2 status return codes */
- +#define C2PORT_INVALID_COMMAND 0x00
- +#define C2PORT_COMMAND_FAILED 0x02
- +#define C2PORT_COMMAND_OK 0x0d
- +
- +/*
- + * C2 port low level signal managements
- + */
- +
- +static void c2port_reset(struct c2port_device *dev)
- +{
- + struct c2port_ops *ops = dev->ops;
- +
- + /* To reset the device we have to keep clock line low for at least
- + * 20us.
- + */
- + local_irq_disable();
- + ops->c2ck_set(dev, 0);
- + udelay(25);
- + ops->c2ck_set(dev, 1);
- + local_irq_enable();
- +
- + udelay(1);
- +}
- +
- +static void c2port_strobe_ck(struct c2port_device *dev)
- +{
- + struct c2port_ops *ops = dev->ops;
- +
- + /* During hi-low-hi transition we disable local IRQs to avoid
- + * interructions since C2 port specification says that it must be
- + * shorter than 5us, otherwise the microcontroller may consider
- + * it as a reset signal!
- + */
- + local_irq_disable();
- + ops->c2ck_set(dev, 0);
- + udelay(1);
- + ops->c2ck_set(dev, 1);
- + local_irq_enable();
- +
- + udelay(1);
- +}
- +
- +/*
- + * C2 port basic functions
- + */
- +
- +static void c2port_write_ar(struct c2port_device *dev, u8 addr)
- +{
- + struct c2port_ops *ops = dev->ops;
- + int i;
- +
- + /* START field */
- + c2port_strobe_ck(dev);
- +
- + /* INS field (11b, LSB first) */
- + ops->c2d_dir(dev, 0);
- + ops->c2d_set(dev, 1);
- + c2port_strobe_ck(dev);
- + ops->c2d_set(dev, 1);
- + c2port_strobe_ck(dev);
- +
- + /* ADDRESS field */
- + for (i = 0; i < 8; i++) {
- + ops->c2d_set(dev, addr & 0x01);
- + c2port_strobe_ck(dev);
- +
- + addr >>= 1;
- + }
- +
- + /* STOP field */
- + ops->c2d_dir(dev, 1);
- + c2port_strobe_ck(dev);
- +}
- +
- +static int c2port_read_ar(struct c2port_device *dev, u8 *addr)
- +{
- + struct c2port_ops *ops = dev->ops;
- + int i;
- +
- + /* START field */
- + c2port_strobe_ck(dev);
- +
- + /* INS field (10b, LSB first) */
- + ops->c2d_dir(dev, 0);
- + ops->c2d_set(dev, 0);
- + c2port_strobe_ck(dev);
- + ops->c2d_set(dev, 1);
- + c2port_strobe_ck(dev);
- +
- + /* ADDRESS field */
- + ops->c2d_dir(dev, 1);
- + *addr = 0;
- + for (i = 0; i < 8; i++) {
- + *addr >>= 1; /* shift in 8-bit ADDRESS field LSB first */
- +
- + c2port_strobe_ck(dev);
- + if (ops->c2d_get(dev))
- + *addr |= 0x80;
- + }
- +
- + /* STOP field */
- + c2port_strobe_ck(dev);
- +
- + return 0;
- +}
- +
- +static int c2port_write_dr(struct c2port_device *dev, u8 data)
- +{
- + struct c2port_ops *ops = dev->ops;
- + int timeout, i;
- +
- + /* START field */
- + c2port_strobe_ck(dev);
- +
- + /* INS field (01b, LSB first) */
- + ops->c2d_dir(dev, 0);
- + ops->c2d_set(dev, 1);
- + c2port_strobe_ck(dev);
- + ops->c2d_set(dev, 0);
- + c2port_strobe_ck(dev);
- +
- + /* LENGTH field (00b, LSB first -> 1 byte) */
- + ops->c2d_set(dev, 0);
- + c2port_strobe_ck(dev);
- + ops->c2d_set(dev, 0);
- + c2port_strobe_ck(dev);
- +
- + /* DATA field */
- + for (i = 0; i < 8; i++) {
- + ops->c2d_set(dev, data & 0x01);
- + c2port_strobe_ck(dev);
- +
- + data >>= 1;
- + }
- +
- + /* WAIT field */
- + ops->c2d_dir(dev, 1);
- + timeout = 20;
- + do {
- + c2port_strobe_ck(dev);
- + if (ops->c2d_get(dev))
- + break;
- +
- + udelay(1);
- + } while (--timeout > 0);
- + if (timeout == 0)
- + return -EIO;
- +
- + /* STOP field */
- + c2port_strobe_ck(dev);
- +
- + return 0;
- +}
- +
- +static int c2port_read_dr(struct c2port_device *dev, u8 *data)
- +{
- + struct c2port_ops *ops = dev->ops;
- + int timeout, i;
- +
- + /* START field */
- + c2port_strobe_ck(dev);
- +
- + /* INS field (00b, LSB first) */
- + ops->c2d_dir(dev, 0);
- + ops->c2d_set(dev, 0);
- + c2port_strobe_ck(dev);
- + ops->c2d_set(dev, 0);
- + c2port_strobe_ck(dev);
- +
- + /* LENGTH field (00b, LSB first -> 1 byte) */
- + ops->c2d_set(dev, 0);
- + c2port_strobe_ck(dev);
- + ops->c2d_set(dev, 0);
- + c2port_strobe_ck(dev);
- +
- + /* WAIT field */
- + ops->c2d_dir(dev, 1);
- + timeout = 20;
- + do {
- + c2port_strobe_ck(dev);
- + if (ops->c2d_get(dev))
- + break;
- +
- + udelay(1);
- + } while (--timeout > 0);
- + if (timeout == 0)
- + return -EIO;
- +
- + /* DATA field */
- + *data = 0;
- + for (i = 0; i < 8; i++) {
- + *data >>= 1; /* shift in 8-bit DATA field LSB first */
- +
- + c2port_strobe_ck(dev);
- + if (ops->c2d_get(dev))
- + *data |= 0x80;
- + }
- +
- + /* STOP field */
- + c2port_strobe_ck(dev);
- +
- + return 0;
- +}
- +
- +static int c2port_poll_in_busy(struct c2port_device *dev)
- +{
- + u8 addr;
- + int ret, timeout = 20;
- +
- + do {
- + ret = (c2port_read_ar(dev, &addr));
- + if (ret < 0)
- + return -EIO;
- +
- + if (!(addr & 0x02))
- + break;
- +
- + udelay(1);
- + } while (--timeout > 0);
- + if (timeout == 0)
- + return -EIO;
- +
- + return 0;
- +}
- +
- +static int c2port_poll_out_ready(struct c2port_device *dev)
- +{
- + u8 addr;
- + int ret, timeout = 10000; /* erase flash needs long time... */
- +
- + do {
- + ret = (c2port_read_ar(dev, &addr));
- + if (ret < 0)
- + return -EIO;
- +
- + if (addr & 0x01)
- + break;
- +
- + udelay(1);
- + } while (--timeout > 0);
- + if (timeout == 0)
- + return -EIO;
- +
- + return 0;
- +}
- +
- +/*
- + * sysfs methods
- + */
- +
- +static ssize_t c2port_show_name(struct device *dev,
- + struct device_attribute *attr, char *buf)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- +
- + return sprintf(buf, "%s\n", c2dev->name);
- +}
- +static DEVICE_ATTR(name, 0444, c2port_show_name, NULL);
- +
- +static ssize_t c2port_show_flash_blocks_num(struct device *dev,
- + struct device_attribute *attr, char *buf)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- + struct c2port_ops *ops = c2dev->ops;
- +
- + return sprintf(buf, "%d\n", ops->blocks_num);
- +}
- +static DEVICE_ATTR(flash_blocks_num, 0444, c2port_show_flash_blocks_num, NULL);
- +
- +static ssize_t c2port_show_flash_block_size(struct device *dev,
- + struct device_attribute *attr, char *buf)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- + struct c2port_ops *ops = c2dev->ops;
- +
- + return sprintf(buf, "%d\n", ops->block_size);
- +}
- +static DEVICE_ATTR(flash_block_size, 0444, c2port_show_flash_block_size, NULL);
- +
- +static ssize_t c2port_show_flash_size(struct device *dev,
- + struct device_attribute *attr, char *buf)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- + struct c2port_ops *ops = c2dev->ops;
- +
- + return sprintf(buf, "%d\n", ops->blocks_num * ops->block_size);
- +}
- +static DEVICE_ATTR(flash_size, 0444, c2port_show_flash_size, NULL);
- +
- +static ssize_t access_show(struct device *dev, struct device_attribute *attr,
- + char *buf)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- +
- + return sprintf(buf, "%d\n", c2dev->access);
- +}
- +
- +static ssize_t access_store(struct device *dev, struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- + struct c2port_ops *ops = c2dev->ops;
- + int status, ret;
- +
- + ret = sscanf(buf, "%d", &status);
- + if (ret != 1)
- + return -EINVAL;
- +
- + mutex_lock(&c2dev->mutex);
- +
- + c2dev->access = !!status;
- +
- + /* If access is "on" clock should be HIGH _before_ setting the line
- + * as output and data line should be set as INPUT anyway */
- + if (c2dev->access)
- + ops->c2ck_set(c2dev, 1);
- + ops->access(c2dev, c2dev->access);
- + if (c2dev->access)
- + ops->c2d_dir(c2dev, 1);
- +
- + mutex_unlock(&c2dev->mutex);
- +
- + return count;
- +}
- +static DEVICE_ATTR_RW(access);
- +
- +static ssize_t c2port_store_reset(struct device *dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- +
- + /* Check the device access status */
- + if (!c2dev->access)
- + return -EBUSY;
- +
- + mutex_lock(&c2dev->mutex);
- +
- + c2port_reset(c2dev);
- + c2dev->flash_access = 0;
- +
- + mutex_unlock(&c2dev->mutex);
- +
- + return count;
- +}
- +static DEVICE_ATTR(reset, 0200, NULL, c2port_store_reset);
- +
- +static ssize_t __c2port_show_dev_id(struct c2port_device *dev, char *buf)
- +{
- + u8 data;
- + int ret;
- +
- + /* Select DEVICEID register for C2 data register accesses */
- + c2port_write_ar(dev, C2PORT_DEVICEID);
- +
- + /* Read and return the device ID register */
- + ret = c2port_read_dr(dev, &data);
- + if (ret < 0)
- + return ret;
- +
- + return sprintf(buf, "%d\n", data);
- +}
- +
- +static ssize_t c2port_show_dev_id(struct device *dev,
- + struct device_attribute *attr, char *buf)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- + ssize_t ret;
- +
- + /* Check the device access status */
- + if (!c2dev->access)
- + return -EBUSY;
- +
- + mutex_lock(&c2dev->mutex);
- + ret = __c2port_show_dev_id(c2dev, buf);
- + mutex_unlock(&c2dev->mutex);
- +
- + if (ret < 0)
- + dev_err(dev, "cannot read from %s\n", c2dev->name);
- +
- + return ret;
- +}
- +static DEVICE_ATTR(dev_id, 0444, c2port_show_dev_id, NULL);
- +
- +static ssize_t __c2port_show_rev_id(struct c2port_device *dev, char *buf)
- +{
- + u8 data;
- + int ret;
- +
- + /* Select REVID register for C2 data register accesses */
- + c2port_write_ar(dev, C2PORT_REVID);
- +
- + /* Read and return the revision ID register */
- + ret = c2port_read_dr(dev, &data);
- + if (ret < 0)
- + return ret;
- +
- + return sprintf(buf, "%d\n", data);
- +}
- +
- +static ssize_t c2port_show_rev_id(struct device *dev,
- + struct device_attribute *attr, char *buf)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- + ssize_t ret;
- +
- + /* Check the device access status */
- + if (!c2dev->access)
- + return -EBUSY;
- +
- + mutex_lock(&c2dev->mutex);
- + ret = __c2port_show_rev_id(c2dev, buf);
- + mutex_unlock(&c2dev->mutex);
- +
- + if (ret < 0)
- + dev_err(c2dev->dev, "cannot read from %s\n", c2dev->name);
- +
- + return ret;
- +}
- +static DEVICE_ATTR(rev_id, 0444, c2port_show_rev_id, NULL);
- +
- +static ssize_t c2port_show_flash_access(struct device *dev,
- + struct device_attribute *attr, char *buf)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- +
- + return sprintf(buf, "%d\n", c2dev->flash_access);
- +}
- +
- +static ssize_t __c2port_store_flash_access(struct c2port_device *dev,
- + int status)
- +{
- + int ret;
- +
- + /* Check the device access status */
- + if (!dev->access)
- + return -EBUSY;
- +
- + dev->flash_access = !!status;
- +
- + /* If flash_access is off we have nothing to do... */
- + if (dev->flash_access == 0)
- + return 0;
- +
- + /* Target the C2 flash programming control register for C2 data
- + * register access */
- + c2port_write_ar(dev, C2PORT_FPCTL);
- +
- + /* Write the first keycode to enable C2 Flash programming */
- + ret = c2port_write_dr(dev, 0x02);
- + if (ret < 0)
- + return ret;
- +
- + /* Write the second keycode to enable C2 Flash programming */
- + ret = c2port_write_dr(dev, 0x01);
- + if (ret < 0)
- + return ret;
- +
- + /* Delay for at least 20ms to ensure the target is ready for
- + * C2 flash programming */
- + mdelay(25);
- +
- + return 0;
- +}
- +
- +static ssize_t c2port_store_flash_access(struct device *dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- + int status;
- + ssize_t ret;
- +
- + ret = sscanf(buf, "%d", &status);
- + if (ret != 1)
- + return -EINVAL;
- +
- + mutex_lock(&c2dev->mutex);
- + ret = __c2port_store_flash_access(c2dev, status);
- + mutex_unlock(&c2dev->mutex);
- +
- + if (ret < 0) {
- + dev_err(c2dev->dev, "cannot enable %s flash programming\n",
- + c2dev->name);
- + return ret;
- + }
- +
- + return count;
- +}
- +static DEVICE_ATTR(flash_access, 0644, c2port_show_flash_access,
- + c2port_store_flash_access);
- +
- +static ssize_t __c2port_write_flash_erase(struct c2port_device *dev)
- +{
- + u8 status;
- + int ret;
- +
- + /* Target the C2 flash programming data register for C2 data register
- + * access.
- + */
- + c2port_write_ar(dev, C2PORT_FPDAT);
- +
- + /* Send device erase command */
- + c2port_write_dr(dev, C2PORT_DEVICE_ERASE);
- +
- + /* Wait for input acknowledge */
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Should check status before starting FLASH access sequence */
- +
- + /* Wait for status information */
- + ret = c2port_poll_out_ready(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Read flash programming interface status */
- + ret = c2port_read_dr(dev, &status);
- + if (ret < 0)
- + return ret;
- + if (status != C2PORT_COMMAND_OK)
- + return -EBUSY;
- +
- + /* Send a three-byte arming sequence to enable the device erase.
- + * If the sequence is not received correctly, the command will be
- + * ignored.
- + * Sequence is: 0xde, 0xad, 0xa5.
- + */
- + c2port_write_dr(dev, 0xde);
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- + c2port_write_dr(dev, 0xad);
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- + c2port_write_dr(dev, 0xa5);
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- +
- + ret = c2port_poll_out_ready(dev);
- + if (ret < 0)
- + return ret;
- +
- + return 0;
- +}
- +
- +static ssize_t c2port_store_flash_erase(struct device *dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + struct c2port_device *c2dev = dev_get_drvdata(dev);
- + int ret;
- +
- + /* Check the device and flash access status */
- + if (!c2dev->access || !c2dev->flash_access)
- + return -EBUSY;
- +
- + mutex_lock(&c2dev->mutex);
- + ret = __c2port_write_flash_erase(c2dev);
- + mutex_unlock(&c2dev->mutex);
- +
- + if (ret < 0) {
- + dev_err(c2dev->dev, "cannot erase %s flash\n", c2dev->name);
- + return ret;
- + }
- +
- + return count;
- +}
- +static DEVICE_ATTR(flash_erase, 0200, NULL, c2port_store_flash_erase);
- +
- +static ssize_t __c2port_read_flash_data(struct c2port_device *dev,
- + char *buffer, loff_t offset, size_t count)
- +{
- + struct c2port_ops *ops = dev->ops;
- + u8 status, nread = 128;
- + int i, ret;
- +
- + /* Check for flash end */
- + if (offset >= ops->block_size * ops->blocks_num)
- + return 0;
- +
- + if (ops->block_size * ops->blocks_num - offset < nread)
- + nread = ops->block_size * ops->blocks_num - offset;
- + if (count < nread)
- + nread = count;
- + if (nread == 0)
- + return nread;
- +
- + /* Target the C2 flash programming data register for C2 data register
- + * access */
- + c2port_write_ar(dev, C2PORT_FPDAT);
- +
- + /* Send flash block read command */
- + c2port_write_dr(dev, C2PORT_BLOCK_READ);
- +
- + /* Wait for input acknowledge */
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Should check status before starting FLASH access sequence */
- +
- + /* Wait for status information */
- + ret = c2port_poll_out_ready(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Read flash programming interface status */
- + ret = c2port_read_dr(dev, &status);
- + if (ret < 0)
- + return ret;
- + if (status != C2PORT_COMMAND_OK)
- + return -EBUSY;
- +
- + /* Send address high byte */
- + c2port_write_dr(dev, offset >> 8);
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Send address low byte */
- + c2port_write_dr(dev, offset & 0x00ff);
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Send address block size */
- + c2port_write_dr(dev, nread);
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Should check status before reading FLASH block */
- +
- + /* Wait for status information */
- + ret = c2port_poll_out_ready(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Read flash programming interface status */
- + ret = c2port_read_dr(dev, &status);
- + if (ret < 0)
- + return ret;
- + if (status != C2PORT_COMMAND_OK)
- + return -EBUSY;
- +
- + /* 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 c2port_read_flash_data(struct file *filp, struct kobject *kobj,
- + struct bin_attribute *attr,
- + char *buffer, loff_t offset, size_t count)
- +{
- + struct c2port_device *c2dev =
- + dev_get_drvdata(container_of(kobj,
- + struct device, kobj));
- + ssize_t ret;
- +
- + /* Check the device and flash access status */
- + if (!c2dev->access || !c2dev->flash_access)
- + return -EBUSY;
- +
- + mutex_lock(&c2dev->mutex);
- + ret = __c2port_read_flash_data(c2dev, buffer, offset, count);
- + mutex_unlock(&c2dev->mutex);
- +
- + if (ret < 0)
- + dev_err(c2dev->dev, "cannot read %s flash\n", c2dev->name);
- +
- + return ret;
- +}
- +
- +static ssize_t __c2port_write_flash_data(struct c2port_device *dev,
- + char *buffer, loff_t offset, size_t count)
- +{
- + struct c2port_ops *ops = dev->ops;
- + u8 status, nwrite = 128;
- + int i, ret;
- +
- + if (nwrite > count)
- + nwrite = count;
- + if (ops->block_size * ops->blocks_num - offset < nwrite)
- + nwrite = ops->block_size * ops->blocks_num - offset;
- +
- + /* Check for flash end */
- + if (offset >= ops->block_size * ops->blocks_num)
- + return -EINVAL;
- +
- + /* Target the C2 flash programming data register for C2 data register
- + * access */
- + c2port_write_ar(dev, C2PORT_FPDAT);
- +
- + /* Send flash block write command */
- + c2port_write_dr(dev, C2PORT_BLOCK_WRITE);
- +
- + /* Wait for input acknowledge */
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Should check status before starting FLASH access sequence */
- +
- + /* Wait for status information */
- + ret = c2port_poll_out_ready(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Read flash programming interface status */
- + ret = c2port_read_dr(dev, &status);
- + if (ret < 0)
- + return ret;
- + if (status != C2PORT_COMMAND_OK)
- + return -EBUSY;
- +
- + /* Send address high byte */
- + c2port_write_dr(dev, offset >> 8);
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Send address low byte */
- + c2port_write_dr(dev, offset & 0x00ff);
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Send address block size */
- + c2port_write_dr(dev, nwrite);
- + ret = c2port_poll_in_busy(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Should check status before writing FLASH block */
- +
- + /* Wait for status information */
- + ret = c2port_poll_out_ready(dev);
- + if (ret < 0)
- + return ret;
- +
- + /* Read flash programming interface status */
- + ret = c2port_read_dr(dev, &status);
- + if (ret < 0)
- + return ret;
- + if (status != C2PORT_COMMAND_OK)
- + return -EBUSY;
- +
- + /* Write flash block */
- + 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;
- +
- + }
- +
- + /* Wait for last flash write to complete */
- + ret = c2port_poll_out_ready(dev);
- + if (ret < 0)
- + return ret;
- +
- + return nwrite;
- +}
- +
- +static ssize_t c2port_write_flash_data(struct file *filp, struct kobject *kobj,
- + struct bin_attribute *attr,
- + char *buffer, loff_t offset, size_t count)
- +{
- + struct c2port_device *c2dev =
- + dev_get_drvdata(container_of(kobj,
- + struct device, kobj));
- + int ret;
- +
- + /* Check the device access status */
- + if (!c2dev->access || !c2dev->flash_access)
- + return -EBUSY;
- +
- + mutex_lock(&c2dev->mutex);
- + ret = __c2port_write_flash_data(c2dev, buffer, offset, count);
- + mutex_unlock(&c2dev->mutex);
- +
- + if (ret < 0)
- + dev_err(c2dev->dev, "cannot write %s flash\n", c2dev->name);
- +
- + return ret;
- +}
- +/* size is computed at run-time */
- +static BIN_ATTR(flash_data, 0644, c2port_read_flash_data,
- + c2port_write_flash_data, 0);
- +
- +/*
- + * Class attributes
- + */
- +static struct attribute *c2port_attrs[] = {
- + &dev_attr_name.attr,
- + &dev_attr_flash_blocks_num.attr,
- + &dev_attr_flash_block_size.attr,
- + &dev_attr_flash_size.attr,
- + &dev_attr_access.attr,
- + &dev_attr_reset.attr,
- + &dev_attr_dev_id.attr,
- + &dev_attr_rev_id.attr,
- + &dev_attr_flash_access.attr,
- + &dev_attr_flash_erase.attr,
- + NULL,
- +};
- +
- +static struct bin_attribute *c2port_bin_attrs[] = {
- + &bin_attr_flash_data,
- + NULL,
- +};
- +
- +static const struct attribute_group c2port_group = {
- + .attrs = c2port_attrs,
- + .bin_attrs = c2port_bin_attrs,
- +};
- +
- +static const struct attribute_group *c2port_groups[] = {
- + &c2port_group,
- + NULL,
- +};
- +
- +/*
- + * Exported functions
- + */
- +
- +struct c2port_device *c2port_device_register(char *name,
- + struct c2port_ops *ops, void *devdata)
- +{
- + struct c2port_device *c2dev;
- + int ret;
- +
- + if (unlikely(!ops) || unlikely(!ops->access) || \
- + unlikely(!ops->c2d_dir) || unlikely(!ops->c2ck_set) || \
- + unlikely(!ops->c2d_get) || unlikely(!ops->c2d_set))
- + return ERR_PTR(-EINVAL);
- +
- + c2dev = kmalloc(sizeof(struct c2port_device), GFP_KERNEL);
- + kmemcheck_annotate_bitfield(c2dev, flags);
- + if (unlikely(!c2dev))
- + return ERR_PTR(-ENOMEM);
- +
- + idr_preload(GFP_KERNEL);
- + spin_lock_irq(&c2port_idr_lock);
- + ret = idr_alloc(&c2port_idr, c2dev, 0, 0, GFP_NOWAIT);
- + spin_unlock_irq(&c2port_idr_lock);
- + idr_preload_end();
- +
- + if (ret < 0)
- + goto error_idr_alloc;
- + c2dev->id = ret;
- +
- + bin_attr_flash_data.size = ops->blocks_num * ops->block_size;
- +
- + c2dev->dev = device_create(c2port_class, NULL, 0, c2dev,
- + "c2port%d", c2dev->id);
- + if (unlikely(IS_ERR(c2dev->dev))) {
- + ret = PTR_ERR(c2dev->dev);
- + goto error_device_create;
- + }
- + dev_set_drvdata(c2dev->dev, c2dev);
- +
- + strncpy(c2dev->name, name, C2PORT_NAME_LEN);
- + c2dev->ops = ops;
- + mutex_init(&c2dev->mutex);
- +
- + /* By default C2 port access is off */
- + c2dev->access = c2dev->flash_access = 0;
- + ops->access(c2dev, 0);
- +
- + dev_info(c2dev->dev, "C2 port %s added\n", name);
- + dev_info(c2dev->dev, "%s flash has %d blocks x %d bytes "
- + "(%d bytes total)\n",
- + name, ops->blocks_num, ops->block_size,
- + ops->blocks_num * ops->block_size);
- +
- + return c2dev;
- +
- +error_device_create:
- + spin_lock_irq(&c2port_idr_lock);
- + idr_remove(&c2port_idr, c2dev->id);
- + spin_unlock_irq(&c2port_idr_lock);
- +
- +error_idr_alloc:
- + kfree(c2dev);
- +
- + return ERR_PTR(ret);
- +}
- +EXPORT_SYMBOL(c2port_device_register);
- +
- +void c2port_device_unregister(struct c2port_device *c2dev)
- +{
- + if (!c2dev)
- + return;
- +
- + dev_info(c2dev->dev, "C2 port %s removed\n", c2dev->name);
- +
- + spin_lock_irq(&c2port_idr_lock);
- + idr_remove(&c2port_idr, c2dev->id);
- + spin_unlock_irq(&c2port_idr_lock);
- +
- + device_destroy(c2port_class, c2dev->id);
- +
- + kfree(c2dev);
- +}
- +EXPORT_SYMBOL(c2port_device_unregister);
- +
- +/*
- + * Module stuff
- + */
- +
- +static int __init c2port_init(void)
- +{
- + printk(KERN_INFO "Silicon Labs C2 port support v. " DRIVER_VERSION
- + " - (C) 2007 Rodolfo Giometti\n");
- +
- + c2port_class = class_create(THIS_MODULE, "c2port");
- + if (IS_ERR(c2port_class)) {
- + printk(KERN_ERR "c2port: failed to allocate class\n");
- + return PTR_ERR(c2port_class);
- + }
- + c2port_class->dev_groups = c2port_groups;
- +
- + return 0;
- +}
- +
- +static void __exit c2port_exit(void)
- +{
- + class_destroy(c2port_class);
- +}
- +
- +module_init(c2port_init);
- +module_exit(c2port_exit);
- +
- +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
- +MODULE_DESCRIPTION("Silicon Labs C2 port support v. " DRIVER_VERSION);
- +MODULE_LICENSE("GPL");
- diff --git a/drivers/misc/c2port/c2port-duramar2150.c b/drivers/misc/c2port/c2port-duramar2150.c
- deleted file mode 100644
- index 5484301..0000000
- --- a/drivers/misc/c2port/c2port-duramar2150.c
- +++ /dev/null
- @@ -1,159 +0,0 @@
- -/*
- - * Silicon Labs C2 port Linux support for Eurotech Duramar 2150
- - *
- - * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it>
- - * Copyright (c) 2008 Eurotech S.p.A. <info@eurotech.it>
- - *
- - * 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
- - * the Free Software Foundation
- - */
- -
- -#include <linux/errno.h>
- -#include <linux/init.h>
- -#include <linux/kernel.h>
- -#include <linux/module.h>
- -#include <linux/delay.h>
- -#include <linux/io.h>
- -#include <linux/ioport.h>
- -#include <linux/c2port.h>
- -
- -#define DATA_PORT 0x325
- -#define DIR_PORT 0x326
- -#define C2D (1 << 0)
- -#define C2CK (1 << 1)
- -
- -static DEFINE_MUTEX(update_lock);
- -
- -/*
- - * C2 port operations
- - */
- -
- -static void duramar2150_c2port_access(struct c2port_device *dev, int status)
- -{
- - u8 v;
- -
- - mutex_lock(&update_lock);
- -
- - v = inb(DIR_PORT);
- -
- - /* 0 = input, 1 = output */
- - if (status)
- - outb(v | (C2D | C2CK), DIR_PORT);
- - else
- - /* When access is "off" is important that both lines are set
- - * as inputs or hi-impedance */
- - outb(v & ~(C2D | C2CK), DIR_PORT);
- -
- - mutex_unlock(&update_lock);
- -}
- -
- -static void duramar2150_c2port_c2d_dir(struct c2port_device *dev, int dir)
- -{
- - u8 v;
- -
- - mutex_lock(&update_lock);
- -
- - v = inb(DIR_PORT);
- -
- - if (dir)
- - outb(v & ~C2D, DIR_PORT);
- - else
- - outb(v | C2D, DIR_PORT);
- -
- - mutex_unlock(&update_lock);
- -}
- -
- -static int duramar2150_c2port_c2d_get(struct c2port_device *dev)
- -{
- - return inb(DATA_PORT) & C2D;
- -}
- -
- -static void duramar2150_c2port_c2d_set(struct c2port_device *dev, int status)
- -{
- - u8 v;
- -
- - mutex_lock(&update_lock);
- -
- - v = inb(DATA_PORT);
- -
- - if (status)
- - outb(v | C2D, DATA_PORT);
- - else
- - outb(v & ~C2D, DATA_PORT);
- -
- - mutex_unlock(&update_lock);
- -}
- -
- -static void duramar2150_c2port_c2ck_set(struct c2port_device *dev, int status)
- -{
- - u8 v;
- -
- - mutex_lock(&update_lock);
- -
- - v = inb(DATA_PORT);
- -
- - if (status)
- - outb(v | C2CK, DATA_PORT);
- - else
- - outb(v & ~C2CK, DATA_PORT);
- -
- - mutex_unlock(&update_lock);
- -}
- -
- -static struct c2port_ops duramar2150_c2port_ops = {
- - .block_size = 512, /* bytes */
- - .blocks_num = 30, /* total flash size: 15360 bytes */
- -
- - .access = duramar2150_c2port_access,
- - .c2d_dir = duramar2150_c2port_c2d_dir,
- - .c2d_get = duramar2150_c2port_c2d_get,
- - .c2d_set = duramar2150_c2port_c2d_set,
- - .c2ck_set = duramar2150_c2port_c2ck_set,
- -};
- -
- -static struct c2port_device *duramar2150_c2port_dev;
- -
- -/*
- - * Module stuff
- - */
- -
- -static int __init duramar2150_c2port_init(void)
- -{
- - struct resource *res;
- - int ret = 0;
- -
- - res = request_region(0x325, 2, "c2port");
- - if (!res)
- - return -EBUSY;
- -
- - duramar2150_c2port_dev = c2port_device_register("uc",
- - &duramar2150_c2port_ops, NULL);
- - if (!duramar2150_c2port_dev) {
- - ret = -ENODEV;
- - goto free_region;
- - }
- -
- - return 0;
- -
- -free_region:
- - release_region(0x325, 2);
- - return ret;
- -}
- -
- -static void __exit duramar2150_c2port_exit(void)
- -{
- - /* Setup the GPIOs as input by default (access = 0) */
- - duramar2150_c2port_access(duramar2150_c2port_dev, 0);
- -
- - c2port_device_unregister(duramar2150_c2port_dev);
- -
- - release_region(0x325, 2);
- -}
- -
- -module_init(duramar2150_c2port_init);
- -module_exit(duramar2150_c2port_exit);
- -
- -MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
- -MODULE_DESCRIPTION("Silicon Labs C2 port Linux support for Duramar 2150");
- -MODULE_LICENSE("GPL");
- diff --git a/drivers/misc/c2port/c2port-gpio.c b/drivers/misc/c2port/c2port-gpio.c
- new file mode 100644
- index 0000000..4928ee5
- --- /dev/null
- +++ b/drivers/misc/c2port/c2port-gpio.c
- @@ -0,0 +1,125 @@
- +/*
- + * Silicon Labs C2 port Linux support for GPIO
- + *
- + * Copyright (c) 2007 Rodolfo Giometti <giometti@linux.it>
- + * Copyright (c) 2007 Eurotech S.p.A. <info@eurotech.it>
- + * Copyright (c) 2014 Gerhard Bertelsmann <info@gerhard-bertelsmann.de>
- + *
- + * 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
- + * the Free Software Foundation
- + */
- +
- +#include <linux/errno.h>
- +#include <linux/init.h>
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/gpio.h>
- +#include <linux/delay.h>
- +
- +#include <linux/c2port.h>
- +
- +static int c2ck = 1;
- +static int c2d = 0;
- +
- +module_param(c2ck, int, S_IRUSR);
- +MODULE_PARM_DESC(c2ck, "C2CK pin");
- +module_param(c2d, int, S_IRUSR);
- +MODULE_PARM_DESC(c2d, "C2D pin");
- +
- +/* --- C2 port operations --------------------------------------------------- */
- +
- +static void gpio_c2port_access(struct c2port_device *dev, int status)
- +{
- + if (status) {
- + gpio_direction_output(c2ck, 1);
- + gpio_direction_output(c2d, 1);
- + } else {
- + /* When access is "off" is important that both lines are set
- + * as inputs or hi-impedence */
- + gpio_direction_input(c2ck);
- + gpio_direction_input(c2d);
- + }
- +}
- +
- +static void gpio_c2port_c2d_dir(struct c2port_device *dev, int dir)
- +{
- + if (dir)
- + gpio_direction_input(c2d);
- + else
- + gpio_direction_output(c2d, gpio_get_value(c2d));
- +}
- +
- +static int gpio_c2port_c2d_get(struct c2port_device *dev)
- +{
- + return gpio_get_value(c2d);
- +}
- +
- +static void gpio_c2port_c2d_set(struct c2port_device *dev, int status)
- +{
- + gpio_set_value(c2d, status);
- +}
- +
- +static void gpio_c2port_c2ck_set(struct c2port_device *dev, int status)
- +{
- + gpio_set_value(c2ck, status);
- +}
- +
- +static struct c2port_ops gpio_c2port_ops = {
- + block_size :512, /* bytes */
- + blocks_num :30, /* total flash size: 15360 bytes */
- + access :gpio_c2port_access,
- + c2d_dir :gpio_c2port_c2d_dir,
- + c2d_get :gpio_c2port_c2d_get,
- + c2d_set :gpio_c2port_c2d_set,
- + c2ck_set :gpio_c2port_c2ck_set,
- +};
- +
- +static struct c2port_device *gpio_c2port_dev;
- +
- +/* --- Module stuff --------------------------------------------------------- */
- +
- +static int __init gpio_c2port_init(void)
- +{
- + int ret;
- +
- + ret = gpio_request(c2ck, "c2port clock");
- + if (ret)
- + goto exit;
- + gpio_direction_input(c2ck);
- +
- + ret = gpio_request(c2d, "c2port data");
- + if (ret)
- + goto free_gpio;
- + gpio_direction_input(c2d);
- +
- + gpio_c2port_dev = c2port_device_register("uc", &gpio_c2port_ops, NULL);
- + if (!gpio_c2port_dev)
- + return -ENODEV;
- +
- + return 0;
- +
- +free_gpio:
- + gpio_free(c2ck);
- +exit:
- + return ret;
- +}
- +
- +static void __exit gpio_c2port_exit(void)
- +{
- + /* Setup the GPIOs as input by default (access = 0) */
- + gpio_c2port_access(gpio_c2port_dev, 0);
- +
- + c2port_device_unregister(gpio_c2port_dev);
- +
- + gpio_free(c2ck);
- + gpio_free(c2d);
- +}
- +
- +module_init(gpio_c2port_init);
- +module_exit(gpio_c2port_exit);
- +
- +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
- +MODULE_AUTHOR("Gerhard Bertelsmann <info@gerhard-bertelsmann.de>");
- +MODULE_DESCRIPTION("Silicon Labs C2 port Linux support for GPIO");
- +MODULE_LICENSE("GPL");
- diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c
- deleted file mode 100644
- index 464419b..0000000
- --- a/drivers/misc/c2port/core.c
- +++ /dev/null
- @@ -1,1009 +0,0 @@
- -/*
- - * Silicon Labs C2 port core Linux support
- - *
- - * Copyright (c) 2007 Rodolfo Giometti <giometti@linux.it>
- - * Copyright (c) 2007 Eurotech S.p.A. <info@eurotech.it>
- - *
- - * 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
- - * the Free Software Foundation
- - */
- -
- -#include <linux/module.h>
- -#include <linux/init.h>
- -#include <linux/device.h>
- -#include <linux/errno.h>
- -#include <linux/err.h>
- -#include <linux/kernel.h>
- -#include <linux/kmemcheck.h>
- -#include <linux/ctype.h>
- -#include <linux/delay.h>
- -#include <linux/idr.h>
- -#include <linux/sched.h>
- -#include <linux/slab.h>
- -
- -#include <linux/c2port.h>
- -
- -#define DRIVER_NAME "c2port"
- -#define DRIVER_VERSION "0.51.0"
- -
- -static DEFINE_SPINLOCK(c2port_idr_lock);
- -static DEFINE_IDR(c2port_idr);
- -
- -/*
- - * Local variables
- - */
- -
- -static struct class *c2port_class;
- -
- -/*
- - * C2 registers & commands defines
- - */
- -
- -/* C2 registers */
- -#define C2PORT_DEVICEID 0x00
- -#define C2PORT_REVID 0x01
- -#define C2PORT_FPCTL 0x02
- -#define C2PORT_FPDAT 0xB4
- -
- -/* C2 interface commands */
- -#define C2PORT_GET_VERSION 0x01
- -#define C2PORT_DEVICE_ERASE 0x03
- -#define C2PORT_BLOCK_READ 0x06
- -#define C2PORT_BLOCK_WRITE 0x07
- -#define C2PORT_PAGE_ERASE 0x08
- -
- -/* C2 status return codes */
- -#define C2PORT_INVALID_COMMAND 0x00
- -#define C2PORT_COMMAND_FAILED 0x02
- -#define C2PORT_COMMAND_OK 0x0d
- -
- -/*
- - * C2 port low level signal managements
- - */
- -
- -static void c2port_reset(struct c2port_device *dev)
- -{
- - struct c2port_ops *ops = dev->ops;
- -
- - /* To reset the device we have to keep clock line low for at least
- - * 20us.
- - */
- - local_irq_disable();
- - ops->c2ck_set(dev, 0);
- - udelay(25);
- - ops->c2ck_set(dev, 1);
- - local_irq_enable();
- -
- - udelay(1);
- -}
- -
- -static void c2port_strobe_ck(struct c2port_device *dev)
- -{
- - struct c2port_ops *ops = dev->ops;
- -
- - /* During hi-low-hi transition we disable local IRQs to avoid
- - * interructions since C2 port specification says that it must be
- - * shorter than 5us, otherwise the microcontroller may consider
- - * it as a reset signal!
- - */
- - local_irq_disable();
- - ops->c2ck_set(dev, 0);
- - udelay(1);
- - ops->c2ck_set(dev, 1);
- - local_irq_enable();
- -
- - udelay(1);
- -}
- -
- -/*
- - * C2 port basic functions
- - */
- -
- -static void c2port_write_ar(struct c2port_device *dev, u8 addr)
- -{
- - struct c2port_ops *ops = dev->ops;
- - int i;
- -
- - /* START field */
- - c2port_strobe_ck(dev);
- -
- - /* INS field (11b, LSB first) */
- - ops->c2d_dir(dev, 0);
- - ops->c2d_set(dev, 1);
- - c2port_strobe_ck(dev);
- - ops->c2d_set(dev, 1);
- - c2port_strobe_ck(dev);
- -
- - /* ADDRESS field */
- - for (i = 0; i < 8; i++) {
- - ops->c2d_set(dev, addr & 0x01);
- - c2port_strobe_ck(dev);
- -
- - addr >>= 1;
- - }
- -
- - /* STOP field */
- - ops->c2d_dir(dev, 1);
- - c2port_strobe_ck(dev);
- -}
- -
- -static int c2port_read_ar(struct c2port_device *dev, u8 *addr)
- -{
- - struct c2port_ops *ops = dev->ops;
- - int i;
- -
- - /* START field */
- - c2port_strobe_ck(dev);
- -
- - /* INS field (10b, LSB first) */
- - ops->c2d_dir(dev, 0);
- - ops->c2d_set(dev, 0);
- - c2port_strobe_ck(dev);
- - ops->c2d_set(dev, 1);
- - c2port_strobe_ck(dev);
- -
- - /* ADDRESS field */
- - ops->c2d_dir(dev, 1);
- - *addr = 0;
- - for (i = 0; i < 8; i++) {
- - *addr >>= 1; /* shift in 8-bit ADDRESS field LSB first */
- -
- - c2port_strobe_ck(dev);
- - if (ops->c2d_get(dev))
- - *addr |= 0x80;
- - }
- -
- - /* STOP field */
- - c2port_strobe_ck(dev);
- -
- - return 0;
- -}
- -
- -static int c2port_write_dr(struct c2port_device *dev, u8 data)
- -{
- - struct c2port_ops *ops = dev->ops;
- - int timeout, i;
- -
- - /* START field */
- - c2port_strobe_ck(dev);
- -
- - /* INS field (01b, LSB first) */
- - ops->c2d_dir(dev, 0);
- - ops->c2d_set(dev, 1);
- - c2port_strobe_ck(dev);
- - ops->c2d_set(dev, 0);
- - c2port_strobe_ck(dev);
- -
- - /* LENGTH field (00b, LSB first -> 1 byte) */
- - ops->c2d_set(dev, 0);
- - c2port_strobe_ck(dev);
- - ops->c2d_set(dev, 0);
- - c2port_strobe_ck(dev);
- -
- - /* DATA field */
- - for (i = 0; i < 8; i++) {
- - ops->c2d_set(dev, data & 0x01);
- - c2port_strobe_ck(dev);
- -
- - data >>= 1;
- - }
- -
- - /* WAIT field */
- - ops->c2d_dir(dev, 1);
- - timeout = 20;
- - do {
- - c2port_strobe_ck(dev);
- - if (ops->c2d_get(dev))
- - break;
- -
- - udelay(1);
- - } while (--timeout > 0);
- - if (timeout == 0)
- - return -EIO;
- -
- - /* STOP field */
- - c2port_strobe_ck(dev);
- -
- - return 0;
- -}
- -
- -static int c2port_read_dr(struct c2port_device *dev, u8 *data)
- -{
- - struct c2port_ops *ops = dev->ops;
- - int timeout, i;
- -
- - /* START field */
- - c2port_strobe_ck(dev);
- -
- - /* INS field (00b, LSB first) */
- - ops->c2d_dir(dev, 0);
- - ops->c2d_set(dev, 0);
- - c2port_strobe_ck(dev);
- - ops->c2d_set(dev, 0);
- - c2port_strobe_ck(dev);
- -
- - /* LENGTH field (00b, LSB first -> 1 byte) */
- - ops->c2d_set(dev, 0);
- - c2port_strobe_ck(dev);
- - ops->c2d_set(dev, 0);
- - c2port_strobe_ck(dev);
- -
- - /* WAIT field */
- - ops->c2d_dir(dev, 1);
- - timeout = 20;
- - do {
- - c2port_strobe_ck(dev);
- - if (ops->c2d_get(dev))
- - break;
- -
- - udelay(1);
- - } while (--timeout > 0);
- - if (timeout == 0)
- - return -EIO;
- -
- - /* DATA field */
- - *data = 0;
- - for (i = 0; i < 8; i++) {
- - *data >>= 1; /* shift in 8-bit DATA field LSB first */
- -
- - c2port_strobe_ck(dev);
- - if (ops->c2d_get(dev))
- - *data |= 0x80;
- - }
- -
- - /* STOP field */
- - c2port_strobe_ck(dev);
- -
- - return 0;
- -}
- -
- -static int c2port_poll_in_busy(struct c2port_device *dev)
- -{
- - u8 addr;
- - int ret, timeout = 20;
- -
- - do {
- - ret = (c2port_read_ar(dev, &addr));
- - if (ret < 0)
- - return -EIO;
- -
- - if (!(addr & 0x02))
- - break;
- -
- - udelay(1);
- - } while (--timeout > 0);
- - if (timeout == 0)
- - return -EIO;
- -
- - return 0;
- -}
- -
- -static int c2port_poll_out_ready(struct c2port_device *dev)
- -{
- - u8 addr;
- - int ret, timeout = 10000; /* erase flash needs long time... */
- -
- - do {
- - ret = (c2port_read_ar(dev, &addr));
- - if (ret < 0)
- - return -EIO;
- -
- - if (addr & 0x01)
- - break;
- -
- - udelay(1);
- - } while (--timeout > 0);
- - if (timeout == 0)
- - return -EIO;
- -
- - return 0;
- -}
- -
- -/*
- - * sysfs methods
- - */
- -
- -static ssize_t c2port_show_name(struct device *dev,
- - struct device_attribute *attr, char *buf)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- -
- - return sprintf(buf, "%s\n", c2dev->name);
- -}
- -static DEVICE_ATTR(name, 0444, c2port_show_name, NULL);
- -
- -static ssize_t c2port_show_flash_blocks_num(struct device *dev,
- - struct device_attribute *attr, char *buf)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- - struct c2port_ops *ops = c2dev->ops;
- -
- - return sprintf(buf, "%d\n", ops->blocks_num);
- -}
- -static DEVICE_ATTR(flash_blocks_num, 0444, c2port_show_flash_blocks_num, NULL);
- -
- -static ssize_t c2port_show_flash_block_size(struct device *dev,
- - struct device_attribute *attr, char *buf)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- - struct c2port_ops *ops = c2dev->ops;
- -
- - return sprintf(buf, "%d\n", ops->block_size);
- -}
- -static DEVICE_ATTR(flash_block_size, 0444, c2port_show_flash_block_size, NULL);
- -
- -static ssize_t c2port_show_flash_size(struct device *dev,
- - struct device_attribute *attr, char *buf)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- - struct c2port_ops *ops = c2dev->ops;
- -
- - return sprintf(buf, "%d\n", ops->blocks_num * ops->block_size);
- -}
- -static DEVICE_ATTR(flash_size, 0444, c2port_show_flash_size, NULL);
- -
- -static ssize_t access_show(struct device *dev, struct device_attribute *attr,
- - char *buf)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- -
- - return sprintf(buf, "%d\n", c2dev->access);
- -}
- -
- -static ssize_t access_store(struct device *dev, struct device_attribute *attr,
- - const char *buf, size_t count)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- - struct c2port_ops *ops = c2dev->ops;
- - int status, ret;
- -
- - ret = sscanf(buf, "%d", &status);
- - if (ret != 1)
- - return -EINVAL;
- -
- - mutex_lock(&c2dev->mutex);
- -
- - c2dev->access = !!status;
- -
- - /* If access is "on" clock should be HIGH _before_ setting the line
- - * as output and data line should be set as INPUT anyway */
- - if (c2dev->access)
- - ops->c2ck_set(c2dev, 1);
- - ops->access(c2dev, c2dev->access);
- - if (c2dev->access)
- - ops->c2d_dir(c2dev, 1);
- -
- - mutex_unlock(&c2dev->mutex);
- -
- - return count;
- -}
- -static DEVICE_ATTR_RW(access);
- -
- -static ssize_t c2port_store_reset(struct device *dev,
- - struct device_attribute *attr,
- - const char *buf, size_t count)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- -
- - /* Check the device access status */
- - if (!c2dev->access)
- - return -EBUSY;
- -
- - mutex_lock(&c2dev->mutex);
- -
- - c2port_reset(c2dev);
- - c2dev->flash_access = 0;
- -
- - mutex_unlock(&c2dev->mutex);
- -
- - return count;
- -}
- -static DEVICE_ATTR(reset, 0200, NULL, c2port_store_reset);
- -
- -static ssize_t __c2port_show_dev_id(struct c2port_device *dev, char *buf)
- -{
- - u8 data;
- - int ret;
- -
- - /* Select DEVICEID register for C2 data register accesses */
- - c2port_write_ar(dev, C2PORT_DEVICEID);
- -
- - /* Read and return the device ID register */
- - ret = c2port_read_dr(dev, &data);
- - if (ret < 0)
- - return ret;
- -
- - return sprintf(buf, "%d\n", data);
- -}
- -
- -static ssize_t c2port_show_dev_id(struct device *dev,
- - struct device_attribute *attr, char *buf)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- - ssize_t ret;
- -
- - /* Check the device access status */
- - if (!c2dev->access)
- - return -EBUSY;
- -
- - mutex_lock(&c2dev->mutex);
- - ret = __c2port_show_dev_id(c2dev, buf);
- - mutex_unlock(&c2dev->mutex);
- -
- - if (ret < 0)
- - dev_err(dev, "cannot read from %s\n", c2dev->name);
- -
- - return ret;
- -}
- -static DEVICE_ATTR(dev_id, 0444, c2port_show_dev_id, NULL);
- -
- -static ssize_t __c2port_show_rev_id(struct c2port_device *dev, char *buf)
- -{
- - u8 data;
- - int ret;
- -
- - /* Select REVID register for C2 data register accesses */
- - c2port_write_ar(dev, C2PORT_REVID);
- -
- - /* Read and return the revision ID register */
- - ret = c2port_read_dr(dev, &data);
- - if (ret < 0)
- - return ret;
- -
- - return sprintf(buf, "%d\n", data);
- -}
- -
- -static ssize_t c2port_show_rev_id(struct device *dev,
- - struct device_attribute *attr, char *buf)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- - ssize_t ret;
- -
- - /* Check the device access status */
- - if (!c2dev->access)
- - return -EBUSY;
- -
- - mutex_lock(&c2dev->mutex);
- - ret = __c2port_show_rev_id(c2dev, buf);
- - mutex_unlock(&c2dev->mutex);
- -
- - if (ret < 0)
- - dev_err(c2dev->dev, "cannot read from %s\n", c2dev->name);
- -
- - return ret;
- -}
- -static DEVICE_ATTR(rev_id, 0444, c2port_show_rev_id, NULL);
- -
- -static ssize_t c2port_show_flash_access(struct device *dev,
- - struct device_attribute *attr, char *buf)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- -
- - return sprintf(buf, "%d\n", c2dev->flash_access);
- -}
- -
- -static ssize_t __c2port_store_flash_access(struct c2port_device *dev,
- - int status)
- -{
- - int ret;
- -
- - /* Check the device access status */
- - if (!dev->access)
- - return -EBUSY;
- -
- - dev->flash_access = !!status;
- -
- - /* If flash_access is off we have nothing to do... */
- - if (dev->flash_access == 0)
- - return 0;
- -
- - /* Target the C2 flash programming control register for C2 data
- - * register access */
- - c2port_write_ar(dev, C2PORT_FPCTL);
- -
- - /* Write the first keycode to enable C2 Flash programming */
- - ret = c2port_write_dr(dev, 0x02);
- - if (ret < 0)
- - return ret;
- -
- - /* Write the second keycode to enable C2 Flash programming */
- - ret = c2port_write_dr(dev, 0x01);
- - if (ret < 0)
- - return ret;
- -
- - /* Delay for at least 20ms to ensure the target is ready for
- - * C2 flash programming */
- - mdelay(25);
- -
- - return 0;
- -}
- -
- -static ssize_t c2port_store_flash_access(struct device *dev,
- - struct device_attribute *attr,
- - const char *buf, size_t count)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- - int status;
- - ssize_t ret;
- -
- - ret = sscanf(buf, "%d", &status);
- - if (ret != 1)
- - return -EINVAL;
- -
- - mutex_lock(&c2dev->mutex);
- - ret = __c2port_store_flash_access(c2dev, status);
- - mutex_unlock(&c2dev->mutex);
- -
- - if (ret < 0) {
- - dev_err(c2dev->dev, "cannot enable %s flash programming\n",
- - c2dev->name);
- - return ret;
- - }
- -
- - return count;
- -}
- -static DEVICE_ATTR(flash_access, 0644, c2port_show_flash_access,
- - c2port_store_flash_access);
- -
- -static ssize_t __c2port_write_flash_erase(struct c2port_device *dev)
- -{
- - u8 status;
- - int ret;
- -
- - /* Target the C2 flash programming data register for C2 data register
- - * access.
- - */
- - c2port_write_ar(dev, C2PORT_FPDAT);
- -
- - /* Send device erase command */
- - c2port_write_dr(dev, C2PORT_DEVICE_ERASE);
- -
- - /* Wait for input acknowledge */
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Should check status before starting FLASH access sequence */
- -
- - /* Wait for status information */
- - ret = c2port_poll_out_ready(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Read flash programming interface status */
- - ret = c2port_read_dr(dev, &status);
- - if (ret < 0)
- - return ret;
- - if (status != C2PORT_COMMAND_OK)
- - return -EBUSY;
- -
- - /* Send a three-byte arming sequence to enable the device erase.
- - * If the sequence is not received correctly, the command will be
- - * ignored.
- - * Sequence is: 0xde, 0xad, 0xa5.
- - */
- - c2port_write_dr(dev, 0xde);
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- - c2port_write_dr(dev, 0xad);
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- - c2port_write_dr(dev, 0xa5);
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- -
- - ret = c2port_poll_out_ready(dev);
- - if (ret < 0)
- - return ret;
- -
- - return 0;
- -}
- -
- -static ssize_t c2port_store_flash_erase(struct device *dev,
- - struct device_attribute *attr,
- - const char *buf, size_t count)
- -{
- - struct c2port_device *c2dev = dev_get_drvdata(dev);
- - int ret;
- -
- - /* Check the device and flash access status */
- - if (!c2dev->access || !c2dev->flash_access)
- - return -EBUSY;
- -
- - mutex_lock(&c2dev->mutex);
- - ret = __c2port_write_flash_erase(c2dev);
- - mutex_unlock(&c2dev->mutex);
- -
- - if (ret < 0) {
- - dev_err(c2dev->dev, "cannot erase %s flash\n", c2dev->name);
- - return ret;
- - }
- -
- - return count;
- -}
- -static DEVICE_ATTR(flash_erase, 0200, NULL, c2port_store_flash_erase);
- -
- -static ssize_t __c2port_read_flash_data(struct c2port_device *dev,
- - char *buffer, loff_t offset, size_t count)
- -{
- - struct c2port_ops *ops = dev->ops;
- - u8 status, nread = 128;
- - int i, ret;
- -
- - /* Check for flash end */
- - if (offset >= ops->block_size * ops->blocks_num)
- - return 0;
- -
- - if (ops->block_size * ops->blocks_num - offset < nread)
- - nread = ops->block_size * ops->blocks_num - offset;
- - if (count < nread)
- - nread = count;
- - if (nread == 0)
- - return nread;
- -
- - /* Target the C2 flash programming data register for C2 data register
- - * access */
- - c2port_write_ar(dev, C2PORT_FPDAT);
- -
- - /* Send flash block read command */
- - c2port_write_dr(dev, C2PORT_BLOCK_READ);
- -
- - /* Wait for input acknowledge */
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Should check status before starting FLASH access sequence */
- -
- - /* Wait for status information */
- - ret = c2port_poll_out_ready(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Read flash programming interface status */
- - ret = c2port_read_dr(dev, &status);
- - if (ret < 0)
- - return ret;
- - if (status != C2PORT_COMMAND_OK)
- - return -EBUSY;
- -
- - /* Send address high byte */
- - c2port_write_dr(dev, offset >> 8);
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Send address low byte */
- - c2port_write_dr(dev, offset & 0x00ff);
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Send address block size */
- - c2port_write_dr(dev, nread);
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Should check status before reading FLASH block */
- -
- - /* Wait for status information */
- - ret = c2port_poll_out_ready(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Read flash programming interface status */
- - ret = c2port_read_dr(dev, &status);
- - if (ret < 0)
- - return ret;
- - if (status != C2PORT_COMMAND_OK)
- - return -EBUSY;
- -
- - /* 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 c2port_read_flash_data(struct file *filp, struct kobject *kobj,
- - struct bin_attribute *attr,
- - char *buffer, loff_t offset, size_t count)
- -{
- - struct c2port_device *c2dev =
- - dev_get_drvdata(container_of(kobj,
- - struct device, kobj));
- - ssize_t ret;
- -
- - /* Check the device and flash access status */
- - if (!c2dev->access || !c2dev->flash_access)
- - return -EBUSY;
- -
- - mutex_lock(&c2dev->mutex);
- - ret = __c2port_read_flash_data(c2dev, buffer, offset, count);
- - mutex_unlock(&c2dev->mutex);
- -
- - if (ret < 0)
- - dev_err(c2dev->dev, "cannot read %s flash\n", c2dev->name);
- -
- - return ret;
- -}
- -
- -static ssize_t __c2port_write_flash_data(struct c2port_device *dev,
- - char *buffer, loff_t offset, size_t count)
- -{
- - struct c2port_ops *ops = dev->ops;
- - u8 status, nwrite = 128;
- - int i, ret;
- -
- - if (nwrite > count)
- - nwrite = count;
- - if (ops->block_size * ops->blocks_num - offset < nwrite)
- - nwrite = ops->block_size * ops->blocks_num - offset;
- -
- - /* Check for flash end */
- - if (offset >= ops->block_size * ops->blocks_num)
- - return -EINVAL;
- -
- - /* Target the C2 flash programming data register for C2 data register
- - * access */
- - c2port_write_ar(dev, C2PORT_FPDAT);
- -
- - /* Send flash block write command */
- - c2port_write_dr(dev, C2PORT_BLOCK_WRITE);
- -
- - /* Wait for input acknowledge */
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Should check status before starting FLASH access sequence */
- -
- - /* Wait for status information */
- - ret = c2port_poll_out_ready(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Read flash programming interface status */
- - ret = c2port_read_dr(dev, &status);
- - if (ret < 0)
- - return ret;
- - if (status != C2PORT_COMMAND_OK)
- - return -EBUSY;
- -
- - /* Send address high byte */
- - c2port_write_dr(dev, offset >> 8);
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Send address low byte */
- - c2port_write_dr(dev, offset & 0x00ff);
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Send address block size */
- - c2port_write_dr(dev, nwrite);
- - ret = c2port_poll_in_busy(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Should check status before writing FLASH block */
- -
- - /* Wait for status information */
- - ret = c2port_poll_out_ready(dev);
- - if (ret < 0)
- - return ret;
- -
- - /* Read flash programming interface status */
- - ret = c2port_read_dr(dev, &status);
- - if (ret < 0)
- - return ret;
- - if (status != C2PORT_COMMAND_OK)
- - return -EBUSY;
- -
- - /* Write flash block */
- - 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;
- -
- - }
- -
- - /* Wait for last flash write to complete */
- - ret = c2port_poll_out_ready(dev);
- - if (ret < 0)
- - return ret;
- -
- - return nwrite;
- -}
- -
- -static ssize_t c2port_write_flash_data(struct file *filp, struct kobject *kobj,
- - struct bin_attribute *attr,
- - char *buffer, loff_t offset, size_t count)
- -{
- - struct c2port_device *c2dev =
- - dev_get_drvdata(container_of(kobj,
- - struct device, kobj));
- - int ret;
- -
- - /* Check the device access status */
- - if (!c2dev->access || !c2dev->flash_access)
- - return -EBUSY;
- -
- - mutex_lock(&c2dev->mutex);
- - ret = __c2port_write_flash_data(c2dev, buffer, offset, count);
- - mutex_unlock(&c2dev->mutex);
- -
- - if (ret < 0)
- - dev_err(c2dev->dev, "cannot write %s flash\n", c2dev->name);
- -
- - return ret;
- -}
- -/* size is computed at run-time */
- -static BIN_ATTR(flash_data, 0644, c2port_read_flash_data,
- - c2port_write_flash_data, 0);
- -
- -/*
- - * Class attributes
- - */
- -static struct attribute *c2port_attrs[] = {
- - &dev_attr_name.attr,
- - &dev_attr_flash_blocks_num.attr,
- - &dev_attr_flash_block_size.attr,
- - &dev_attr_flash_size.attr,
- - &dev_attr_access.attr,
- - &dev_attr_reset.attr,
- - &dev_attr_dev_id.attr,
- - &dev_attr_rev_id.attr,
- - &dev_attr_flash_access.attr,
- - &dev_attr_flash_erase.attr,
- - NULL,
- -};
- -
- -static struct bin_attribute *c2port_bin_attrs[] = {
- - &bin_attr_flash_data,
- - NULL,
- -};
- -
- -static const struct attribute_group c2port_group = {
- - .attrs = c2port_attrs,
- - .bin_attrs = c2port_bin_attrs,
- -};
- -
- -static const struct attribute_group *c2port_groups[] = {
- - &c2port_group,
- - NULL,
- -};
- -
- -/*
- - * Exported functions
- - */
- -
- -struct c2port_device *c2port_device_register(char *name,
- - struct c2port_ops *ops, void *devdata)
- -{
- - struct c2port_device *c2dev;
- - int ret;
- -
- - if (unlikely(!ops) || unlikely(!ops->access) || \
- - unlikely(!ops->c2d_dir) || unlikely(!ops->c2ck_set) || \
- - unlikely(!ops->c2d_get) || unlikely(!ops->c2d_set))
- - return ERR_PTR(-EINVAL);
- -
- - c2dev = kmalloc(sizeof(struct c2port_device), GFP_KERNEL);
- - kmemcheck_annotate_bitfield(c2dev, flags);
- - if (unlikely(!c2dev))
- - return ERR_PTR(-ENOMEM);
- -
- - idr_preload(GFP_KERNEL);
- - spin_lock_irq(&c2port_idr_lock);
- - ret = idr_alloc(&c2port_idr, c2dev, 0, 0, GFP_NOWAIT);
- - spin_unlock_irq(&c2port_idr_lock);
- - idr_preload_end();
- -
- - if (ret < 0)
- - goto error_idr_alloc;
- - c2dev->id = ret;
- -
- - bin_attr_flash_data.size = ops->blocks_num * ops->block_size;
- -
- - c2dev->dev = device_create(c2port_class, NULL, 0, c2dev,
- - "c2port%d", c2dev->id);
- - if (unlikely(IS_ERR(c2dev->dev))) {
- - ret = PTR_ERR(c2dev->dev);
- - goto error_device_create;
- - }
- - dev_set_drvdata(c2dev->dev, c2dev);
- -
- - strncpy(c2dev->name, name, C2PORT_NAME_LEN);
- - c2dev->ops = ops;
- - mutex_init(&c2dev->mutex);
- -
- - /* By default C2 port access is off */
- - c2dev->access = c2dev->flash_access = 0;
- - ops->access(c2dev, 0);
- -
- - dev_info(c2dev->dev, "C2 port %s added\n", name);
- - dev_info(c2dev->dev, "%s flash has %d blocks x %d bytes "
- - "(%d bytes total)\n",
- - name, ops->blocks_num, ops->block_size,
- - ops->blocks_num * ops->block_size);
- -
- - return c2dev;
- -
- -error_device_create:
- - spin_lock_irq(&c2port_idr_lock);
- - idr_remove(&c2port_idr, c2dev->id);
- - spin_unlock_irq(&c2port_idr_lock);
- -
- -error_idr_alloc:
- - kfree(c2dev);
- -
- - return ERR_PTR(ret);
- -}
- -EXPORT_SYMBOL(c2port_device_register);
- -
- -void c2port_device_unregister(struct c2port_device *c2dev)
- -{
- - if (!c2dev)
- - return;
- -
- - dev_info(c2dev->dev, "C2 port %s removed\n", c2dev->name);
- -
- - spin_lock_irq(&c2port_idr_lock);
- - idr_remove(&c2port_idr, c2dev->id);
- - spin_unlock_irq(&c2port_idr_lock);
- -
- - device_destroy(c2port_class, c2dev->id);
- -
- - kfree(c2dev);
- -}
- -EXPORT_SYMBOL(c2port_device_unregister);
- -
- -/*
- - * Module stuff
- - */
- -
- -static int __init c2port_init(void)
- -{
- - printk(KERN_INFO "Silicon Labs C2 port support v. " DRIVER_VERSION
- - " - (C) 2007 Rodolfo Giometti\n");
- -
- - c2port_class = class_create(THIS_MODULE, "c2port");
- - if (IS_ERR(c2port_class)) {
- - printk(KERN_ERR "c2port: failed to allocate class\n");
- - return PTR_ERR(c2port_class);
- - }
- - c2port_class->dev_groups = c2port_groups;
- -
- - return 0;
- -}
- -
- -static void __exit c2port_exit(void)
- -{
- - class_destroy(c2port_class);
- -}
- -
- -module_init(c2port_init);
- -module_exit(c2port_exit);
- -
- -MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
- -MODULE_DESCRIPTION("Silicon Labs C2 port support v. " DRIVER_VERSION);
- -MODULE_LICENSE("GPL");
- --
- 1.7.9.5
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement