cns3xxx: remove 2.6.31 support

SVN-Revision: 31418
This commit is contained in:
Gabor Juhos 2012-04-21 16:42:05 +00:00
parent b2b965a696
commit 26b3cdad1c
12 changed files with 0 additions and 57263 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,341 +0,0 @@
--- /dev/null
+++ b/drivers/hwmon/gsp.c
@@ -0,0 +1,310 @@
+/*
+ * A hwmon driver for the Gateworks System Peripheral
+ * Copyright (C) 2009 Gateworks Corporation
+ *
+ * Author: Chris Lang <clang@gateworks.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 - version 2.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+
+
+#define DRV_VERSION "0.2"
+
+enum chips { gsp };
+
+/* AD7418 registers */
+#define GSP_REG_TEMP_IN 0x00
+#define GSP_REG_VIN 0x02
+#define GSP_REG_3P3 0x05
+#define GSP_REG_BAT 0x08
+#define GSP_REG_5P0 0x0b
+#define GSP_REG_CORE 0x0e
+#define GSP_REG_CPU1 0x11
+#define GSP_REG_CPU2 0x14
+#define GSP_REG_DRAM 0x17
+#define GSP_REG_EXT_BAT 0x1a
+#define GSP_REG_IO1 0x1d
+#define GSP_REG_IO2 0x20
+#define GSP_REG_PCIE 0x23
+#define GSP_REG_CURRENT 0x26
+#define GSP_FAN_0 0x2C
+#define GSP_FAN_1 0x2E
+#define GSP_FAN_2 0x30
+#define GSP_FAN_3 0x32
+#define GSP_FAN_4 0x34
+#define GSP_FAN_5 0x36
+
+struct gsp_sensor_info {
+ const char* name;
+ int reg;
+};
+
+static const struct gsp_sensor_info gsp_sensors[] = {
+ {"temp", GSP_REG_TEMP_IN},
+ {"vin", GSP_REG_VIN},
+ {"3p3", GSP_REG_3P3},
+ {"bat", GSP_REG_BAT},
+ {"5p0", GSP_REG_5P0},
+ {"core", GSP_REG_CORE},
+ {"cpu1", GSP_REG_CPU1},
+ {"cpu2", GSP_REG_CPU2},
+ {"dram", GSP_REG_DRAM},
+ {"ext_bat", GSP_REG_EXT_BAT},
+ {"io1", GSP_REG_IO1},
+ {"io2", GSP_REG_IO2},
+ {"pci2", GSP_REG_PCIE},
+ {"current", GSP_REG_CURRENT},
+ {"fan_point0", GSP_FAN_0},
+ {"fan_point1", GSP_FAN_1},
+ {"fan_point2", GSP_FAN_2},
+ {"fan_point3", GSP_FAN_3},
+ {"fan_point4", GSP_FAN_4},
+ {"fan_point5", GSP_FAN_5},
+};
+
+struct gsp_data {
+ struct device *hwmon_dev;
+ struct attribute_group attrs;
+ enum chips type;
+};
+
+static int gsp_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int gsp_remove(struct i2c_client *client);
+
+static const struct i2c_device_id gsp_id[] = {
+ { "gsp", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, gsp_id);
+
+static struct i2c_driver gsp_driver = {
+ .driver = {
+ .name = "gsp",
+ },
+ .probe = gsp_probe,
+ .remove = gsp_remove,
+ .id_table = gsp_id,
+};
+
+/* All registers are word-sized, except for the configuration registers.
+ * AD7418 uses a high-byte first convention. Do NOT use those functions to
+ * access the configuration registers CONF and CONF2, as they are byte-sized.
+ */
+static inline int gsp_read(struct i2c_client *client, u8 reg)
+{
+ unsigned int adc = 0;
+ if (reg == GSP_REG_TEMP_IN || reg > GSP_REG_CURRENT)
+ {
+ adc |= i2c_smbus_read_byte_data(client, reg);
+ adc |= i2c_smbus_read_byte_data(client, reg + 1) << 8;
+ return adc;
+ }
+ else
+ {
+ adc |= i2c_smbus_read_byte_data(client, reg);
+ adc |= i2c_smbus_read_byte_data(client, reg + 1) << 8;
+ adc |= i2c_smbus_read_byte_data(client, reg + 2) << 16;
+ return adc;
+ }
+}
+
+static inline int gsp_write(struct i2c_client *client, u8 reg, u16 value)
+{
+ i2c_smbus_write_byte_data(client, reg, value & 0xff);
+ i2c_smbus_write_byte_data(client, reg + 1, ((value >> 8) & 0xff));
+ return 1;
+}
+
+static ssize_t show_adc(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i2c_client *client = to_i2c_client(dev);
+ return sprintf(buf, "%d\n", gsp_read(client, gsp_sensors[attr->index].reg));
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return sprintf(buf, "%s\n", gsp_sensors[attr->index].name);
+}
+
+static ssize_t store_fan(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ u16 val;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i2c_client *client = to_i2c_client(dev);
+ val = simple_strtoul(buf, NULL, 10);
+ gsp_write(client, gsp_sensors[attr->index].reg, val);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp0_input, S_IRUGO, show_adc, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp0_label, S_IRUGO, show_label, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_adc, NULL, 1);
+static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_adc, NULL, 2);
+static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_adc, NULL, 3);
+static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_adc, NULL, 4);
+static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_adc, NULL, 5);
+static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_label, NULL, 5);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_adc, NULL, 6);
+static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_label, NULL, 6);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_adc, NULL, 7);
+static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_label, NULL, 7);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_adc, NULL, 8);
+static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 8);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_adc, NULL, 9);
+static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 9);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_adc, NULL, 10);
+static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 10);
+static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_adc, NULL, 11);
+static SENSOR_DEVICE_ATTR(in10_label, S_IRUGO, show_label, NULL, 11);
+static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_adc, NULL, 12);
+static SENSOR_DEVICE_ATTR(in11_label, S_IRUGO, show_label, NULL, 12);
+static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_adc, NULL, 13);
+static SENSOR_DEVICE_ATTR(in12_label, S_IRUGO, show_label, NULL, 13);
+
+static SENSOR_DEVICE_ATTR(fan0_point0, S_IRUGO | S_IWUSR, show_adc, store_fan, 14);
+static SENSOR_DEVICE_ATTR(fan0_point1, S_IRUGO | S_IWUSR, show_adc, store_fan, 15);
+static SENSOR_DEVICE_ATTR(fan0_point2, S_IRUGO | S_IWUSR, show_adc, store_fan, 16);
+static SENSOR_DEVICE_ATTR(fan0_point3, S_IRUGO | S_IWUSR, show_adc, store_fan, 17);
+static SENSOR_DEVICE_ATTR(fan0_point4, S_IRUGO | S_IWUSR, show_adc, store_fan, 18);
+static SENSOR_DEVICE_ATTR(fan0_point5, S_IRUGO | S_IWUSR, show_adc, store_fan, 19);
+
+
+
+static struct attribute *gsp_attributes[] = {
+ &sensor_dev_attr_temp0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp0_label.dev_attr.attr,
+ &sensor_dev_attr_in0_label.dev_attr.attr,
+ &sensor_dev_attr_in1_label.dev_attr.attr,
+ &sensor_dev_attr_in2_label.dev_attr.attr,
+ &sensor_dev_attr_in3_label.dev_attr.attr,
+ &sensor_dev_attr_in4_label.dev_attr.attr,
+ &sensor_dev_attr_in5_label.dev_attr.attr,
+ &sensor_dev_attr_in6_label.dev_attr.attr,
+ &sensor_dev_attr_in7_label.dev_attr.attr,
+ &sensor_dev_attr_in8_label.dev_attr.attr,
+ &sensor_dev_attr_in9_label.dev_attr.attr,
+ &sensor_dev_attr_in10_label.dev_attr.attr,
+ &sensor_dev_attr_in11_label.dev_attr.attr,
+ &sensor_dev_attr_in12_label.dev_attr.attr,
+
+ &sensor_dev_attr_fan0_point0.dev_attr.attr,
+ &sensor_dev_attr_fan0_point1.dev_attr.attr,
+ &sensor_dev_attr_fan0_point2.dev_attr.attr,
+ &sensor_dev_attr_fan0_point3.dev_attr.attr,
+ &sensor_dev_attr_fan0_point4.dev_attr.attr,
+ &sensor_dev_attr_fan0_point5.dev_attr.attr,
+
+ NULL
+};
+
+
+static int gsp_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct gsp_data *data;
+ int err;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ err = -EOPNOTSUPP;
+ goto exit;
+ }
+
+ if (!(data = kzalloc(sizeof(struct gsp_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(client, data);
+
+ data->type = id->driver_data;
+
+ switch (data->type) {
+ case 0:
+ data->attrs.attrs = gsp_attributes;
+ break;
+ }
+
+ dev_info(&client->dev, "%s chip found\n", client->name);
+
+ /* Register sysfs hooks */
+ if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs)))
+ goto exit_free;
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ return 0;
+
+exit_remove:
+ sysfs_remove_group(&client->dev.kobj, &data->attrs);
+exit_free:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int gsp_remove(struct i2c_client *client)
+{
+ struct gsp_data *data = i2c_get_clientdata(client);
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &data->attrs);
+ kfree(data);
+ return 0;
+}
+
+static int __init gsp_init(void)
+{
+ return i2c_add_driver(&gsp_driver);
+}
+
+static void __exit gsp_exit(void)
+{
+ i2c_del_driver(&gsp_driver);
+}
+
+MODULE_AUTHOR("Chris Lang <clang@gateworks.com>");
+MODULE_DESCRIPTION("GSP HWMON driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_init(gsp_init);
+module_exit(gsp_exit);
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -57,6 +57,15 @@ config SENSORS_ABITUGURU3
This driver can also be built as a module. If so, the module
will be called abituguru3.
+config SENSORS_GSP
+ tristate "Gateworks System Peripheral"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for the Gateworks System Peripherals.
+
+ This driver can also be built as a module. If so, the module
+ will be called gsp.
+
config SENSORS_AD7414
tristate "Analog Devices AD7414"
depends on I2C && EXPERIMENTAL
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_W83791D) += w83791d
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
+obj-$(CONFIG_SENSORS_GSP) += gsp.o
obj-$(CONFIG_SENSORS_AD7414) += ad7414.o
obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o

View file

@ -1,416 +0,0 @@
--- /dev/null
+++ b/drivers/i2c/busses/i2c-cns3xxx.c
@@ -0,0 +1,388 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <mach/pm.h>
+
+/*
+ * We need the memory map
+ */
+
+#include <mach/board.h>
+
+#define MISC_MEM_MAP_VALUE(reg_offset) (*((uint32_t volatile *)(CNS3XXX_MISC_BASE_VIRT + reg_offset)))
+#define MISC_IOCDB_CTRL MISC_MEM_MAP_VALUE(0x020)
+
+#define I2C_MEM_MAP_ADDR(x) (CNS3XXX_SSP_BASE_VIRT + x)
+#define I2C_MEM_MAP_VALUE(x) (*((unsigned int volatile*)I2C_MEM_MAP_ADDR(x)))
+
+#define I2C_CONTROLLER_REG I2C_MEM_MAP_VALUE(0x20)
+#define I2C_TIME_OUT_REG I2C_MEM_MAP_VALUE(0x24)
+#define I2C_SLAVE_ADDRESS_REG I2C_MEM_MAP_VALUE(0x28)
+#define I2C_WRITE_DATA_REG I2C_MEM_MAP_VALUE(0x2C)
+#define I2C_READ_DATA_REG I2C_MEM_MAP_VALUE(0x30)
+#define I2C_INTERRUPT_STATUS_REG I2C_MEM_MAP_VALUE(0x34)
+#define I2C_INTERRUPT_ENABLE_REG I2C_MEM_MAP_VALUE(0x38)
+#define I2C_TWI_OUT_DLY_REG I2C_MEM_MAP_VALUE(0x3C)
+
+#define I2C_BUS_ERROR_FLAG (0x1)
+#define I2C_ACTION_DONE_FLAG (0x2)
+
+#define CNS3xxx_I2C_ENABLE() (I2C_CONTROLLER_REG) |= ((unsigned int)0x1 << 31)
+#define CNS3xxx_I2C_DISABLE() (I2C_CONTROLLER_REG) &= ~((unsigned int)0x1 << 31)
+#define CNS3xxx_I2C_ENABLE_INTR() (I2C_INTERRUPT_ENABLE_REG) |= 0x03
+#define CNS3xxx_I2C_DISABLE_INTR() (I2C_INTERRUPT_ENABLE_REG) &= 0xfc
+
+#define TWI_TIMEOUT (10*HZ)
+#define I2C_100KHZ 100000
+#define I2C_200KHZ 200000
+#define I2C_300KHZ 300000
+#define I2C_400KHZ 400000
+
+#define CNS3xxx_I2C_CLK I2C_100KHZ
+
+#define STATE_DONE 1
+#define STATE_ERROR 2
+
+struct cns3xxx_i2c {
+ void __iomem *base;
+ wait_queue_head_t wait;
+ struct i2c_adapter adap;
+ struct i2c_msg *msg;
+ int state; /* see STATE_ */
+ int rd_wr_len;
+ u8 *buf;
+};
+
+static u32 cns3xxx_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+cns3xxx_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg)
+{
+ struct cns3xxx_i2c *i2c = i2c_get_adapdata(adap);
+ int i, j;
+ u8 buf[1] = { 0 };
+
+ if (msg->len == 0) {
+ /*
+ * We are probably doing a probe for a device here,
+ * so set the length to one, and data to 0
+ */
+ msg->len = 1;
+ i2c->buf = buf;
+ } else {
+ i2c->buf = msg->buf;
+ }
+
+ if (msg->flags & I2C_M_TEN) {
+ printk
+ ("%s:%d: Presently the driver does not handle extended addressing\n",
+ __FUNCTION__, __LINE__);
+ return -EINVAL;
+ }
+ i2c->msg = msg;
+
+ for (i = 0; i < msg->len; i++) {
+ if (msg->len - i >= 4)
+ i2c->rd_wr_len = 3;
+ else
+ i2c->rd_wr_len = msg->len - i - 1;
+
+ // Set Data Width and TWI_EN
+ I2C_CONTROLLER_REG = 0x80000000 | (i2c->rd_wr_len << 2) | (i2c->rd_wr_len);
+
+ // Clear Write Reg
+ I2C_WRITE_DATA_REG = 0;
+
+ // Set the slave address
+ I2C_SLAVE_ADDRESS_REG = (msg->addr << 1);
+
+ // Are we Writing
+ if (!(msg->flags & I2C_M_RD)) {
+ I2C_CONTROLLER_REG |= (1 << 4);
+ if (i != 0) {
+ /*
+ * We need to set the address in the first byte.
+ * The base address is going to be in buf[0] and then
+ * it needs to be incremented by i - 1.
+ */
+ i2c->buf--;
+ *i2c->buf = buf[0] + i - 1;
+
+ if (i2c->rd_wr_len < 3) {
+ i += i2c->rd_wr_len;
+ i2c->rd_wr_len++;
+ I2C_CONTROLLER_REG = 0x80000000 | (1 << 4) | (i2c->rd_wr_len << 2) | (i2c->rd_wr_len);
+ } else {
+ i += i2c->rd_wr_len - 1;
+ }
+ } else {
+ i += i2c->rd_wr_len;
+ buf[0] = *i2c->buf;
+ }
+ for (j = 0; j <= i2c->rd_wr_len; j++) {
+ I2C_WRITE_DATA_REG |= ((*i2c->buf++) << (8 * j));
+ }
+ } else {
+ i += i2c->rd_wr_len;
+ }
+
+ // Start the Transfer
+ i2c->state = 0; // Clear out the State
+ I2C_CONTROLLER_REG |= (1 << 6);
+
+ if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
+ (i2c->state == STATE_DONE), TWI_TIMEOUT)) {
+ if (i2c->state == STATE_ERROR) {
+ return -EIO;
+ }
+ } else {
+ return -ETIMEDOUT;
+ }
+ }
+ return 0;
+}
+
+static int
+cns3xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ int i;
+ int ret;
+ for (i = 0; i < num; i++)
+ {
+ ret = cns3xxx_i2c_xfer_msg(adap, &msgs[i]);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ return num;
+}
+
+
+static struct i2c_algorithm cns3xxx_i2c_algo = {
+ .master_xfer = cns3xxx_i2c_xfer,
+ .functionality = cns3xxx_i2c_func,
+};
+
+static struct i2c_adapter cns3xxx_i2c_adapter = {
+ .owner = THIS_MODULE,
+ .algo = &cns3xxx_i2c_algo,
+ .algo_data = NULL,
+ .nr = 0,
+ .name = "CNS3xxx I2C 0",
+ .retries = 5,
+};
+
+static void cns3xxx_i2c_adapter_init(struct cns3xxx_i2c *i2c)
+{
+
+ /* Steps
+ * 1. Check if the power is enabled to the module (PMU_BASE + 0x010)
+ * 2. Enable the clock (Enabled by default (PMU doc
+ * but check clk status anyway PMU_BASE + 0X00C)
+ * 3. Configure the registers of i2c
+ */
+
+ // if (!CNS3xxx_I2C_POWER_ON())
+// CNS3xxx_I2C_POWER_ENABLE();
+
+ // if (!CNS3xxx_I2C_CLOCK())
+ // CNS3xxx_I2C_CLOCK_ENABLE();
+
+ cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_SPI_PCM_I2C);
+ cns3xxx_pwr_power_up(0x1 << PM_CLK_GATE_REG_OFFSET_SPI_PCM_I2C);
+ cns3xxx_pwr_soft_rst(0x1 << PM_CLK_GATE_REG_OFFSET_SPI_PCM_I2C);
+
+ /* Disable the I2C */
+ I2C_CONTROLLER_REG = 0; /* Disabled the I2C */
+
+ //enable SCL and SDA which share pin with GPIOB_PIN_EN(0x18)
+ //GPIOB[12]: SCL
+ //GPIOB[13]: SDA
+ (*(u32*)(CNS3XXX_MISC_BASE_VIRT+0x18)) |= ((1<<12)|(1<<13));
+
+ MISC_IOCDB_CTRL &= ~0x300;
+ MISC_IOCDB_CTRL |= 0x300; //21mA...
+
+ /* Check the Reg Dump when testing */
+ I2C_TIME_OUT_REG =
+ ((((((cns3xxx_cpu_clock()*(1000000/8)) / (2 * CNS3xxx_I2C_CLK)) -
+ 1) & 0x3FF) << 8) | (1 << 7) | 0x7F);
+ I2C_TWI_OUT_DLY_REG |= 0x3;
+
+ /* Enable The Interrupt */
+ CNS3xxx_I2C_ENABLE_INTR();
+
+ /* Clear Interrupt Status (0x2 | 0x1) */
+ I2C_INTERRUPT_STATUS_REG |= (I2C_ACTION_DONE_FLAG | I2C_BUS_ERROR_FLAG);
+
+ /* Enable the I2C Controller */
+ CNS3xxx_I2C_ENABLE();
+}
+
+static irqreturn_t cns3xxx_i2c_isr(int irq, void *dev_id)
+{
+ struct cns3xxx_i2c *i2c = dev_id;
+ int i;
+ uint32_t stat = I2C_INTERRUPT_STATUS_REG;
+
+ /* Clear Interrupt */
+ I2C_INTERRUPT_STATUS_REG |= 0x1;
+
+ if (stat & I2C_BUS_ERROR_FLAG) {
+ i2c->state = STATE_ERROR;
+ } else {
+ if (i2c->msg->flags & I2C_M_RD) {
+ for (i = 0; i <= i2c->rd_wr_len; i++)
+ {
+ *i2c->buf++ = ((I2C_READ_DATA_REG >> (8 * i)) & 0xff);
+ }
+ }
+ i2c->state = STATE_DONE;
+ }
+ wake_up(&i2c->wait);
+ return IRQ_HANDLED;
+}
+
+static int __devinit cns3xxx_i2c_probe(struct platform_device *pdev)
+{
+ struct cns3xxx_i2c *i2c;
+ struct resource *res, *res2;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ printk("%s: IORESOURCE_MEM not defined \n", __FUNCTION__);
+ return -ENODEV;
+ }
+
+ res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res2) {
+ printk("%s: IORESOURCE_IRQ not defined \n", __FUNCTION__);
+ return -ENODEV;
+ }
+
+ i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name)) {
+ dev_err(&pdev->dev, "Memory region busy\n");
+ ret = -EBUSY;
+ goto request_mem_failed;
+ }
+
+ i2c->base = ioremap(res->start, res->end - res->start + 1);
+ if (!i2c->base) {
+ dev_err(&pdev->dev, "Unable to map registers\n");
+ ret = -EIO;
+ goto map_failed;
+ }
+
+ cns3xxx_i2c_adapter_init(i2c);
+
+ init_waitqueue_head(&i2c->wait);
+ ret = request_irq(res2->start, cns3xxx_i2c_isr, 0, pdev->name, i2c);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot claim IRQ\n");
+ goto request_irq_failed;
+ }
+
+ platform_set_drvdata(pdev, i2c);
+ i2c->adap = cns3xxx_i2c_adapter;
+ i2c_set_adapdata(&i2c->adap, i2c);
+ i2c->adap.dev.parent = &pdev->dev;
+
+ /* add i2c adapter to i2c tree */
+ ret = i2c_add_numbered_adapter(&i2c->adap);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add adapter\n");
+ goto add_adapter_failed;
+ }
+
+ return 0;
+
+ add_adapter_failed:
+ free_irq(res2->start, i2c);
+ request_irq_failed:
+ iounmap(i2c->base);
+ map_failed:
+ release_mem_region(res->start, res->end - res->start + 1);
+ request_mem_failed:
+ kfree(i2c);
+
+ return ret;
+}
+
+static int __devexit cns3xxx_i2c_remove(struct platform_device *pdev)
+{
+ struct cns3xxx_i2c *i2c = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ /* disable i2c logic */
+ CNS3xxx_I2C_DISABLE_INTR();
+ CNS3xxx_I2C_DISABLE();
+ /* remove adapter & data */
+ i2c_del_adapter(&i2c->adap);
+ platform_set_drvdata(pdev, NULL);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res)
+ free_irq(res->start, i2c);
+
+ iounmap(i2c->base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ kfree(i2c);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+#warning "CONFIG_PM defined: suspend and resume not implemented"
+#define cns3xxx_i2c_suspend NULL
+#define cns3xxx_i2c_resume NULL
+#else
+#define cns3xxx_i2c_suspend NULL
+#define cns3xxx_i2c_resume NULL
+#endif
+
+static struct platform_driver cns3xxx_i2c_driver = {
+ .probe = cns3xxx_i2c_probe,
+ .remove = cns3xxx_i2c_remove,
+ .suspend = cns3xxx_i2c_suspend,
+ .resume = cns3xxx_i2c_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "cns3xxx-i2c",
+ },
+};
+
+static int __init cns3xxx_i2c_init(void)
+{
+ return platform_driver_register(&cns3xxx_i2c_driver);
+}
+
+static void __exit cns3xxx_i2c_exit(void)
+{
+ platform_driver_unregister(&cns3xxx_i2c_driver);
+}
+
+module_init(cns3xxx_i2c_init);
+module_exit(cns3xxx_i2c_exit);
+
+MODULE_AUTHOR("Cavium Networks");
+MODULE_DESCRIPTION("Cavium CNS3XXX I2C Controller");
+MODULE_LICENSE("GPL");
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -422,6 +422,12 @@ config I2C_MV64XXX
This driver can also be built as a module. If so, the module
will be called i2c-mv64xxx.
+config I2C_CNS3XXX
+ tristate "Cavium Networks CNS3XXX I2C Controller"
+ depends on ARCH_CNS3XXX
+ help
+ Supports the Cavium Networks CNS3XXX on-chip I2C interfaces
+
config I2C_OCORES
tristate "OpenCores I2C Controller"
depends on EXPERIMENTAL
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
+obj-$(CONFIG_I2C_CNS3XXX) += i2c-cns3xxx.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o

View file

@ -1,438 +0,0 @@
--- a/crypto/xor.c
+++ b/crypto/xor.c
@@ -25,6 +25,26 @@
/* The xor routines to use. */
static struct xor_block_template *active_template;
+#ifdef CONFIG_CNS3XXX_RAID
+extern void do_cns_rdma_xorgen(unsigned int src_no, unsigned int bytes,
+ void **bh_ptr, void *dst_ptr);
+/**
+ * xor_blocks - one pass xor
+ * @src_count: source count
+ * @bytes: length in bytes
+ * @dest: dest
+ * @srcs: srcs
+ *
+ * Desc:
+ * 1. dest = xor(srcs[0...src_count-1]) within one calc
+ * 2. don't care if dest also be placed in srcs list or not.
+ */
+void xor_blocks(unsigned int src_count, unsigned int bytes, void *dest,
+ void **srcs)
+{
+ do_cns_rdma_xorgen(src_count, bytes, srcs, dest);
+}
+#else
void
xor_blocks(unsigned int src_count, unsigned int bytes, void *dest, void **srcs)
{
@@ -51,6 +71,7 @@ xor_blocks(unsigned int src_count, unsig
p4 = (unsigned long *) srcs[3];
active_template->do_5(bytes, dest, p1, p2, p3, p4);
}
+#endif /* CONFIG_CNS3XXX_RAID */
EXPORT_SYMBOL(xor_blocks);
/* Set of all registered templates. */
@@ -95,7 +116,11 @@ do_xor_speed(struct xor_block_template *
speed / 1000, speed % 1000);
}
+#ifdef CONFIG_CNS3XXX_RAID
+int
+#else
static int __init
+#endif /* CONFIG_CNS3XXX_RAID */
calibrate_xor_blocks(void)
{
void *b1, *b2;
@@ -139,7 +164,10 @@ calibrate_xor_blocks(void)
if (f->speed > fastest->speed)
fastest = f;
}
-
+#ifdef CONFIG_CNS3XXX_RAID
+ /* preferred */
+ fastest = template_list;
+#endif /* CONFIG_CNS3XXX_RAID */
printk(KERN_INFO "xor: using function: %s (%d.%03d MB/sec)\n",
fastest->name, fastest->speed / 1000, fastest->speed % 1000);
@@ -151,10 +179,20 @@ calibrate_xor_blocks(void)
return 0;
}
-static __exit void xor_exit(void) { }
+#ifndef CONFIG_CNS3XXX_RAID
+static __exit void xor_exit(void)
+{
+}
+#endif /* ! CONFIG_CNS3XXX_RAID */
MODULE_LICENSE("GPL");
+#ifdef CONFIG_CNS3XXX_RAID
+/*
+ * Calibrate in R5 init.
+ */
+#else
/* when built-in xor.o must initialize before drivers/md/md.o */
core_initcall(calibrate_xor_blocks);
module_exit(xor_exit);
+#endif /* ! CONFIG_CNS3XXX_RAID */
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -17,7 +17,7 @@ raid6_pq-y += raid6algos.o raid6recov.o
raid6int8.o raid6int16.o raid6int32.o \
raid6altivec1.o raid6altivec2.o raid6altivec4.o \
raid6altivec8.o \
- raid6mmx.o raid6sse1.o raid6sse2.o
+ raid6mmx.o raid6sse1.o raid6sse2.o raid6cns.o
hostprogs-y += mktables
# Note: link order is important. All raid personalities
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -1817,11 +1817,30 @@ static void compute_block_2(struct strip
compute_parity6(sh, UPDATE_PARITY);
return;
} else {
+#ifdef CONFIG_CNS3XXX_RAID
+ void *ptrs[disks];
+
+ count = 0;
+ i = d0_idx;
+ do {
+ ptrs[count++] = page_address(sh->dev[i].page);
+ i = raid6_next_disk(i, disks);
+ if (i != dd_idx1 && i != dd_idx2 &&
+ !test_bit(R5_UPTODATE, &sh->dev[i].flags))
+ printk
+ ("compute_2 with missing block %d/%d\n",
+ count, i);
+ } while (i != d0_idx);
+
+ raid6_dataq_recov(disks, STRIPE_SIZE, faila, ptrs);
+#else
+
/* We're missing D+Q; recompute D from P */
compute_block_1(sh, ((dd_idx1 == sh->qd_idx) ?
dd_idx2 : dd_idx1),
0);
compute_parity6(sh, UPDATE_PARITY); /* Is this necessary? */
+#endif /* CONFIG_CNS3XXX_RAID */
return;
}
}
@@ -5412,8 +5431,21 @@ static struct mdk_personality raid4_pers
.quiesce = raid5_quiesce,
};
+#ifdef CONFIG_CNS3XXX_RAID
+extern int calibrate_xor_blocks(void);
+#endif /* CONFIG_CNS3XXX_RAID */
+
static int __init raid5_init(void)
{
+
+#ifdef CONFIG_CNS3XXX_RAID
+ /* Just execute calibrate xor blocks */
+ int e;
+ e = calibrate_xor_blocks();
+ if (e)
+ return e;
+#endif /* CONFIG_CNS3XXX_RAID */
+
register_md_personality(&raid6_personality);
register_md_personality(&raid5_personality);
register_md_personality(&raid4_personality);
--- a/drivers/md/raid6algos.c
+++ b/drivers/md/raid6algos.c
@@ -49,6 +49,9 @@ extern const struct raid6_calls raid6_al
extern const struct raid6_calls raid6_altivec2;
extern const struct raid6_calls raid6_altivec4;
extern const struct raid6_calls raid6_altivec8;
+#ifdef CONFIG_CNS3XXX_RAID
+extern const struct raid6_calls raid6_cns_raid;
+#endif /* CONFIG_CNS3XXX_RAID */
const struct raid6_calls * const raid6_algos[] = {
&raid6_intx1,
@@ -78,6 +81,11 @@ const struct raid6_calls * const raid6_a
&raid6_altivec4,
&raid6_altivec8,
#endif
+#ifdef CONFIG_CNS3XXX_RAID
+ /* CNS3000 HW RAID acceleration */
+ &raid6_cns_raid,
+#endif /* CONFIG_CNS3XXX_RAID */
+
NULL
};
@@ -125,7 +133,9 @@ int __init raid6_select_algo(void)
if ( !(*algo)->valid || (*algo)->valid() ) {
perf = 0;
+#ifndef CONFIG_CNS3XXX_RAID
preempt_disable();
+#endif
j0 = jiffies;
while ( (j1 = jiffies) == j0 )
cpu_relax();
@@ -134,7 +144,9 @@ int __init raid6_select_algo(void)
(*algo)->gen_syndrome(disks, PAGE_SIZE, dptrs);
perf++;
}
+#ifndef CONFIG_CNS3XXX_RAID
preempt_enable();
+#endif
if ( (*algo)->prefer > bestprefer ||
((*algo)->prefer == bestprefer &&
--- /dev/null
+++ b/drivers/md/raid6cns.c
@@ -0,0 +1,38 @@
+/*
+ * raid6cns.c
+ *
+ * CNS3xxx xor & gen_syndrome functions
+ *
+ */
+
+#ifdef CONFIG_CNS3XXX_RAID
+
+#include <linux/raid/pq.h>
+
+extern void do_cns_rdma_gfgen(unsigned int src_no, unsigned int bytes, void **bh_ptr,
+ void *p_dst, void *q_dst);
+
+/**
+ * raid6_cnsraid_gen_syndrome - CNSRAID Syndrome Generate
+ *
+ * @disks: raid disks
+ * @bytes: length
+ * @ptrs: already arranged stripe ptrs,
+ * disk0=[0], diskNNN=[disks-3],
+ * P/Q=[z0+1] & [z0+2], or, [disks-2], [disks-1]
+ */
+static void raid6_cnsraid_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ do_cns_rdma_gfgen(disks - 2, bytes, ptrs, ptrs[disks-2], ptrs[disks-1]);
+}
+
+const struct raid6_calls raid6_cns_raid = {
+ raid6_cnsraid_gen_syndrome, /* callback */
+ NULL, /* always valid */
+ "CNS-RAID", /* name */
+ 1 /* preferred: revise it to "0" to compare/compete with others algos */
+};
+
+EXPORT_SYMBOL(raid6_cns_raid);
+
+#endif /* CONFIG_CNS3XXX_RAID */
--- a/drivers/md/raid6recov.c
+++ b/drivers/md/raid6recov.c
@@ -20,6 +20,136 @@
#include <linux/raid/pq.h>
+#ifdef CONFIG_CNS3XXX_RAID
+#define R6_RECOV_PD 1
+#define R6_RECOV_DD 2
+#define R6_RECOV_DQ 3
+extern void do_cns_rdma_gfgen_pd_dd_dq(unsigned int src_no, unsigned int bytes,
+ void **bh_ptr, void *w1_dst,
+ void *w2_dst, int pd_dd_qd,
+ unsigned int w1_idx, unsigned int w2_idx,
+ unsigned int *src_idx);
+
+/**
+ * @disks: nr_disks
+ * @bytes: len
+ * @faila: 1st failed DD
+ * @ptrs: ptrs by order {d0, d1, ..., da, ..., dn, P, Q}
+ *
+ * Desc:
+ * new_read_ptrs = {d0, d1, ... dn, Q}
+ * dd1 = faila
+ * p_dst = P
+ */
+void raid6_datap_recov(int disks, size_t bytes, int faila, void **ptrs)
+{
+ int cnt = 0;
+ int count = 0;
+ void *p_dst, *q;
+ void *dd1_dst;
+ void *new_read_ptrs[disks - 2];
+ unsigned int read_idx[disks - 2];
+
+ q = ptrs[disks - 1];
+ p_dst = ptrs[disks - 2];
+ dd1_dst = ptrs[faila];
+
+ while (cnt < disks) {
+ if (cnt != faila && cnt != disks - 2) {
+ new_read_ptrs[count] = ptrs[cnt];
+ read_idx[count] = cnt;
+ count++;
+ }
+ cnt++;
+ }
+
+ do_cns_rdma_gfgen_pd_dd_dq(disks - 2, bytes,
+ new_read_ptrs, p_dst, dd1_dst,
+ R6_RECOV_PD, disks - 1, faila + 1, read_idx);
+}
+
+/**
+ * @disks: nr_disks
+ * @bytes: len
+ * @faila: 1st failed DD
+ * @failb: 2nd failed DD
+ * @ptrs: ptrs by order {d0, d1, ..., da, ..., db, ..., dn, P, Q}
+ *
+ * Desc:
+ * new_read_ptrs = {d0, d1, ... dn, P, Q}
+ * dd1_dst = faila
+ * dd2_dst = failb
+ */
+void raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
+ void **ptrs)
+{
+
+ int cnt = 0;
+ int count = 0;
+ void *p, *q;
+ void *dd1_dst, *dd2_dst;
+ void *new_read_ptrs[disks - 2];
+ unsigned int read_idx[disks - 2];
+
+ q = ptrs[disks - 1];
+ p = ptrs[disks - 2];
+ dd1_dst = ptrs[faila];
+ dd2_dst = ptrs[failb];
+
+ while (cnt < disks) {
+ if (cnt != faila && cnt != failb) {
+ new_read_ptrs[count] = ptrs[cnt];
+ read_idx[count] = cnt;
+ count++;
+ }
+ cnt++;
+ }
+
+ do_cns_rdma_gfgen_pd_dd_dq(disks - 2, bytes,
+ new_read_ptrs, dd1_dst, dd2_dst,
+ R6_RECOV_DD, faila + 1, failb + 1, read_idx);
+}
+
+/**
+ * @disks: nr_disks
+ * @bytes: len
+ * @faila: 1st failed DD
+ * @ptrs: ptrs by order {d0, d1, ..., da, ..., dn, P, Q}
+ *
+ * Desc:
+ * new_read_ptrs = {d0, d1, ... dn, P}
+ * dd1 = faila
+ * q_dst = Q
+ */
+void raid6_dataq_recov(int disks, size_t bytes, int faila, void **ptrs)
+{
+ int cnt = 0;
+ int count = 0;
+ void *q_dst, *p;
+ void *dd1_dst;
+ void *new_read_ptrs[disks - 2];
+ unsigned int read_idx[disks - 2];
+
+ p = ptrs[disks - 2];
+ q_dst = ptrs[disks - 1];
+ dd1_dst = ptrs[faila];
+
+ while (cnt < disks) {
+ if (cnt != faila && cnt != disks - 1) {
+ new_read_ptrs[count] = ptrs[cnt];
+ read_idx[count] = cnt;
+ count++;
+ }
+ cnt++;
+ }
+
+ do_cns_rdma_gfgen_pd_dd_dq(disks - 2, bytes,
+ new_read_ptrs, dd1_dst, q_dst,
+ R6_RECOV_DQ, faila + 1, disks, read_idx);
+}
+
+#else /* CONFIG_CNS3XXX_RAID
+
/* Recover two failed data blocks. */
void raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
void **ptrs)
@@ -96,6 +226,7 @@ void raid6_datap_recov(int disks, size_t
}
}
EXPORT_SYMBOL_GPL(raid6_datap_recov);
+#endif /* CONFIG_CNS3XXX_RAID */
#ifndef __KERNEL__
/* Testing only */
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -100,6 +100,9 @@ void raid6_2data_recov(int disks, size_t
void raid6_datap_recov(int disks, size_t bytes, int faila, void **ptrs);
void raid6_dual_recov(int disks, size_t bytes, int faila, int failb,
void **ptrs);
+#ifdef CONFIG_CNS3XXX_RAID
+void raid6_dataq_recov(int disks, size_t bytes, int faila, void **ptrs);
+#endif /* CONFIG_CNS3XXX_RAID */
/* Some definitions to allow code to be compiled for testing in userspace */
#ifndef __KERNEL__
--- a/include/linux/raid/xor.h
+++ b/include/linux/raid/xor.h
@@ -1,7 +1,11 @@
#ifndef _XOR_H
#define _XOR_H
+#ifdef CONFIG_CNS3XXX_RAID
+#define MAX_XOR_BLOCKS 32
+#else
#define MAX_XOR_BLOCKS 4
+#endif /* CONFIG_CNS3XXX_RAID */
extern void xor_blocks(unsigned int count, unsigned int bytes,
void *dest, void **srcs);
--- a/mm/mempool.c
+++ b/mm/mempool.c
@@ -250,6 +250,28 @@ repeat_alloc:
}
EXPORT_SYMBOL(mempool_alloc);
+#ifdef CONFIG_CNS3XXX_RAID
+/**
+ * acs_mempool_alloc - allocate an element from a specific memory pool
+ * @pool: pointer to the memory pool which was allocated via
+ * mempool_create().
+ *
+ * this function differs from mempool_alloc by directly allocating an element
+ * from @pool without calling @pool->alloc().
+ */
+void *acs_mempool_alloc(mempool_t * pool)
+{
+ unsigned long flags;
+ void *element = NULL;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ if (likely(pool->curr_nr))
+ element = remove_element(pool);
+ spin_unlock_irqrestore(&pool->lock, flags);
+ return element;
+}
+#endif /* CONFIG_CNS3XXX_RAID */
+
/**
* mempool_free - return an element to the pool.
* @element: pool element pointer.

View file

@ -1,496 +0,0 @@
--- /dev/null
+++ b/drivers/watchdog/cns3xxx_wdt.c
@@ -0,0 +1,465 @@
+/*******************************************************************************
+ *
+ * drivers/watchdog/cns3xxx_wdt.c
+ *
+ * Watchdog timer driver for the CNS3XXX SOCs
+ *
+ * Author: Scott Shu
+ *
+ * Copyright (c) 2008 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or
+ * visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ *
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/hardware/arm_twd.h>
+
+struct cns3xxx_wdt {
+ unsigned long timer_alive;
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ unsigned int perturb;
+ char expect_close;
+};
+
+static struct platform_device *cns3xxx_wdt_dev;
+extern unsigned int twd_timer_rate;
+static spinlock_t wdt_lock;
+
+#define TIMER_MARGIN 60
+static int cns3xxx_margin = TIMER_MARGIN;
+module_param(cns3xxx_margin, int, 0);
+MODULE_PARM_DESC(cns3xxx_margin,
+ "CNS3XXX timer margin in seconds. (0 < cns3xxx_margin < 65536, default="
+ __MODULE_STRING(TIMER_MARGIN) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#define ONLY_TESTING 0
+static int cns3xxx_noboot = ONLY_TESTING;
+module_param(cns3xxx_noboot, int, 0);
+MODULE_PARM_DESC(cns3xxx_noboot, "CNS3XXX watchdog action, "
+ "set to 1 to ignore reboots, 0 to reboot (default="
+ __MODULE_STRING(ONLY_TESTING) ")");
+
+/*
+ * This is the interrupt handler. Note that we only use this
+ * in testing mode, so don't actually do a reboot here.
+ */
+static irqreturn_t cns3xxx_wdt_fire(int irq, void *arg)
+{
+ struct cns3xxx_wdt *wdt = arg;
+
+ /* Check it really was our interrupt */
+ if (readl(wdt->base + TWD_WDOG_INTSTAT)) {
+ dev_printk(KERN_CRIT, wdt->dev,
+ "Triggered - Reboot ignored.\n");
+ /* Clear the interrupt on the watchdog */
+ writel(1, wdt->base + TWD_WDOG_INTSTAT);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+/*
+ * cns3xxx_wdt_keepalive - reload the timer
+ *
+ * Note that the spec says a DIFFERENT value must be written to the reload
+ * register each time. The "perturb" variable deals with this by adding 1
+ * to the count every other time the function is called.
+ */
+static void cns3xxx_wdt_keepalive(struct cns3xxx_wdt *wdt)
+{
+ unsigned int count;
+
+ /* Assume prescale is set to 256 */
+ count = (twd_timer_rate / 256) * cns3xxx_margin;
+
+ /* Reload the counter */
+ spin_lock(&wdt_lock);
+ writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
+ wdt->perturb = wdt->perturb ? 0 : 1;
+ spin_unlock(&wdt_lock);
+}
+
+static void cns3xxx_wdt_stop(struct cns3xxx_wdt *wdt)
+{
+ spin_lock(&wdt_lock);
+ writel(0x12345678, wdt->base + TWD_WDOG_DISABLE);
+ writel(0x87654321, wdt->base + TWD_WDOG_DISABLE);
+ writel(0x0, wdt->base + TWD_WDOG_CONTROL);
+ spin_unlock(&wdt_lock);
+}
+
+static void cns3xxx_wdt_start(struct cns3xxx_wdt *wdt)
+{
+ dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n");
+
+ //spin_lock(&wdt_lock);
+ /* This loads the count register but does NOT start the count yet */
+ cns3xxx_wdt_keepalive(wdt);
+ spin_lock(&wdt_lock);
+
+ if (cns3xxx_noboot) {
+ /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */
+ writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL);
+ } else {
+ /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */
+ writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL);
+ }
+ spin_unlock(&wdt_lock);
+}
+
+static int cns3xxx_wdt_set_heartbeat(int t)
+{
+ if (t < 0x0001 || t > 0xFFFF)
+ return -EINVAL;
+
+ cns3xxx_margin = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+static int cns3xxx_wdt_open(struct inode *inode, struct file *file)
+{
+ struct cns3xxx_wdt *wdt = platform_get_drvdata(cns3xxx_wdt_dev);
+
+ if (test_and_set_bit(0, &wdt->timer_alive))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ file->private_data = wdt;
+
+ /*
+ * Activate timer
+ */
+ cns3xxx_wdt_start(wdt);
+
+ return nonseekable_open(inode, file);
+}
+
+static int cns3xxx_wdt_release(struct inode *inode, struct file *file)
+{
+ struct cns3xxx_wdt *wdt = file->private_data;
+
+ /*
+ * Shut off the timer.
+ * Lock it in if it's a module and we set nowayout
+ */
+ if (wdt->expect_close == 42)
+ cns3xxx_wdt_stop(wdt);
+ else {
+ dev_printk(KERN_CRIT, wdt->dev,
+ "unexpected close, not stopping watchdog!\n");
+ cns3xxx_wdt_keepalive(wdt);
+ }
+ clear_bit(0, &wdt->timer_alive);
+ wdt->expect_close = 0;
+ return 0;
+}
+
+static ssize_t cns3xxx_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ struct cns3xxx_wdt *wdt = file->private_data;
+
+ /*
+ * Refresh the timer.
+ */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ wdt->expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ wdt->expect_close = 42;
+ }
+ }
+ cns3xxx_wdt_keepalive(wdt);
+ }
+ return len;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .identity = "CNS3XXX Watchdog",
+};
+
+static long cns3xxx_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cns3xxx_wdt *wdt = file->private_data;
+ int ret;
+ union {
+ struct watchdog_info ident;
+ int i;
+ } uarg;
+
+ if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
+ return -ENOTTY;
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
+ if (ret)
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ uarg.ident = ident;
+ ret = 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ uarg.i = 0;
+ ret = 0;
+ break;
+
+ case WDIOC_SETOPTIONS:
+ ret = -EINVAL;
+ if (uarg.i & WDIOS_DISABLECARD) {
+ cns3xxx_wdt_stop(wdt);
+ ret = 0;
+ }
+ if (uarg.i & WDIOS_ENABLECARD) {
+ cns3xxx_wdt_start(wdt);
+ ret = 0;
+ }
+ break;
+
+ case WDIOC_KEEPALIVE:
+ cns3xxx_wdt_keepalive(wdt);
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = cns3xxx_wdt_set_heartbeat(uarg.i);
+ if (ret)
+ break;
+
+ cns3xxx_wdt_keepalive(wdt);
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ uarg.i = cns3xxx_margin;
+ ret = 0;
+ break;
+
+ default:
+ return -ENOTTY;
+ }
+
+ if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
+ ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd));
+ if (ret)
+ ret = -EFAULT;
+ }
+ return ret;
+}
+
+/*
+ * System shutdown handler. Turn off the watchdog if we're
+ * restarting or halting the system.
+ */
+static void cns3xxx_wdt_shutdown(struct platform_device *dev)
+{
+ struct cns3xxx_wdt *wdt = platform_get_drvdata(dev);
+
+ if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT)
+ cns3xxx_wdt_stop(wdt);
+}
+
+/*
+ * Kernel Interfaces
+ */
+static const struct file_operations cns3xxx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = cns3xxx_wdt_write,
+ .unlocked_ioctl = cns3xxx_wdt_ioctl,
+ .open = cns3xxx_wdt_open,
+ .release = cns3xxx_wdt_release,
+};
+
+static struct miscdevice cns3xxx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &cns3xxx_wdt_fops,
+};
+
+static int __devinit cns3xxx_wdt_probe(struct platform_device *dev)
+{
+ struct cns3xxx_wdt *wdt;
+ struct resource *res;
+ int ret;
+
+ /* We only accept one device, and it must have an id of -1 */
+ if (dev->id != -1)
+ return -ENODEV;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ wdt = kzalloc(sizeof(struct cns3xxx_wdt), GFP_KERNEL);
+ if (!wdt) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ wdt->dev = &dev->dev;
+ wdt->irq = platform_get_irq(dev, 0);
+ if (wdt->irq < 0) {
+ ret = -ENXIO;
+ goto err_free;
+ }
+ wdt->base = ioremap(res->start, res->end - res->start + 1);
+ if (!wdt->base) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ cns3xxx_wdt_miscdev.parent = &dev->dev;
+ ret = misc_register(&cns3xxx_wdt_miscdev);
+ if (ret) {
+ dev_printk(KERN_ERR, wdt->dev,
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto err_misc;
+ }
+
+ ret = request_irq(wdt->irq, cns3xxx_wdt_fire, IRQF_DISABLED,
+ dev->name, wdt);
+ if (ret) {
+ dev_printk(KERN_ERR, wdt->dev,
+ "cannot register IRQ%d for watchdog\n", wdt->irq);
+ goto err_irq;
+ }
+
+ cns3xxx_wdt_stop(wdt);
+ platform_set_drvdata(dev, wdt);
+ cns3xxx_wdt_dev = dev;
+
+ return 0;
+
+err_irq:
+ misc_deregister(&cns3xxx_wdt_miscdev);
+err_misc:
+ platform_set_drvdata(dev, NULL);
+ iounmap(wdt->base);
+err_free:
+ kfree(wdt);
+err_out:
+ return ret;
+}
+
+static int __devexit cns3xxx_wdt_remove(struct platform_device *dev)
+{
+ struct cns3xxx_wdt *wdt = platform_get_drvdata(dev);
+
+ platform_set_drvdata(dev, NULL);
+
+ misc_deregister(&cns3xxx_wdt_miscdev);
+
+ cns3xxx_wdt_dev = NULL;
+
+ free_irq(wdt->irq, wdt);
+ iounmap(wdt->base);
+ kfree(wdt);
+ return 0;
+}
+
+
+static struct platform_driver cns3xxx_wdt_driver = {
+ .probe = cns3xxx_wdt_probe,
+ .remove = __devexit_p(cns3xxx_wdt_remove),
+ .shutdown = cns3xxx_wdt_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "cns3xxx-wdt",
+ },
+};
+
+static char banner[] __initdata = KERN_INFO
+ "CNS3XXX Watchdog Timer, noboot=%d margin=%d sec (nowayout= %d)\n";
+
+static int __init cns3xxx_wdt_init(void)
+{
+ /*
+ * Check that the margin value is within it's range;
+ * if not reset to the default
+ */
+ if (cns3xxx_wdt_set_heartbeat(cns3xxx_margin)) {
+ cns3xxx_wdt_set_heartbeat(TIMER_MARGIN);
+ printk(KERN_INFO "cns3xxx_margin value must be 0 < cns3xxx_margin < 65536, using %d\n",
+ TIMER_MARGIN);
+ }
+
+ printk(banner, cns3xxx_noboot, cns3xxx_margin, nowayout);
+
+ spin_lock_init(&wdt_lock);
+
+ return platform_driver_register(&cns3xxx_wdt_driver);
+}
+
+static void __exit cns3xxx_wdt_exit(void)
+{
+ platform_driver_unregister(&cns3xxx_wdt_driver);
+}
+
+module_init(cns3xxx_wdt_init);
+module_exit(cns3xxx_wdt_exit);
+
+MODULE_AUTHOR("Scott Shu");
+MODULE_DESCRIPTION("CNS3XXX Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:cns3xxx-wdt");
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -231,6 +231,15 @@ config DAVINCI_WATCHDOG
NOTE: once enabled, this timer cannot be disabled.
Say N if you are unsure.
+config CNS3XXX_WATCHDOG
+ tristate "CNS3XXX watchdog"
+ depends on ARCH_CNS3XXX && LOCAL_TIMERS
+ help
+ Watchdog timer embedded into the CNS3XXX SoCs system.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cns3xxx_wdt.
+
config ORION_WATCHDOG
tristate "Orion watchdog"
depends on ARCH_ORION5X || ARCH_KIRKWOOD
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
+obj-$(CONFIG_CNS3XXX_WATCHDOG) += cns3xxx_wdt.o
obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o

View file

@ -1,39 +0,0 @@
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -1743,6 +1743,7 @@ static void serial8250_backup_timeout(un
unsigned int iir, ier = 0, lsr;
unsigned long flags;
+ spin_lock_irqsave(&up->port.lock, flags);
/*
* Must disable interrupts or else we risk racing with the interrupt
* based handler.
@@ -1760,10 +1761,8 @@ static void serial8250_backup_timeout(un
* the "Diva" UART used on the management processor on many HP
* ia64 and parisc boxes.
*/
- spin_lock_irqsave(&up->port.lock, flags);
lsr = serial_in(up, UART_LSR);
up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
- spin_unlock_irqrestore(&up->port.lock, flags);
if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) &&
(!uart_circ_empty(&up->port.info->xmit) || up->port.x_char) &&
(lsr & UART_LSR_THRE)) {
@@ -1771,12 +1770,14 @@ static void serial8250_backup_timeout(un
iir |= UART_IIR_THRI;
}
- if (!(iir & UART_IIR_NO_INT))
- serial8250_handle_port(up);
-
if (is_real_interrupt(up->port.irq))
serial_out(up, UART_IER, ier);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ if (!(iir & UART_IIR_NO_INT))
+ serial8250_handle_port(up);
+
/* Standard timer interval plus 0.2s to keep the port running */
mod_timer(&up->timer,
jiffies + poll_timeout(up->port.timeout) + HZ / 5);

View file

@ -1,21 +0,0 @@
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -350,7 +350,8 @@ static inline dma_addr_t dma_map_page(st
static inline void dma_unmap_single(struct device *dev, dma_addr_t handle,
size_t size, enum dma_data_direction dir)
{
- /* nothing to do */
+ if (dir != DMA_TO_DEVICE)
+ dma_cache_maint(dma_to_virt(dev, handle), size, DMA_FROM_DEVICE);
}
#endif /* CONFIG_DMABOUNCE */
@@ -398,6 +399,8 @@ static inline void dma_sync_single_range
{
BUG_ON(!valid_dma_direction(dir));
+ if (dir != DMA_TO_DEVICE)
+ dma_cache_maint(dma_to_virt(dev, handle) + offset, size, DMA_FROM_DEVICE);
dmabounce_sync_for_cpu(dev, handle, offset, size, dir);
}