936749c44f
* Add new CIG device CS6436-54P and CS5435-54P, also update code for CS6436-56P * security kernel update to 4.9.189 for CIG devices * security kernel update to 4.9.189 for CIG devices * Update rules Update rule file
2210 lines
54 KiB
C
2210 lines
54 KiB
C
/*
|
|
* A hwmon driver for the CIG cs5435-54P CPLD
|
|
*
|
|
* Copyright (C) 2018 Cambridge, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/module.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/isa.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/io.h>
|
|
#include <asm/irq.h>
|
|
#include "i2c-algo-lpc.h"
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/types.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/device.h>
|
|
#include <linux/cdev.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/atomic.h>
|
|
#include <linux/i2c-mux.h>
|
|
#include <linux/list.h>
|
|
#include <linux/dmi.h>
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#ifndef CPLD_USER
|
|
# include <linux/ioctl.h>
|
|
#else
|
|
# include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#include <linux/input.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <net/sock.h>
|
|
#include <linux/netlink.h>
|
|
#include <linux/rtc.h>
|
|
|
|
|
|
|
|
|
|
|
|
/********************************************** Start ********************************************************/
|
|
|
|
/*
|
|
* ISA bus.
|
|
*/
|
|
|
|
static void platform_isa_bus_release(struct device * dev)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
|
|
static struct device isa_bus = {
|
|
.init_name = "lpc-isa",
|
|
.release = platform_isa_bus_release,
|
|
};
|
|
|
|
struct isa_dev {
|
|
struct device dev;
|
|
struct device *next;
|
|
unsigned int id;
|
|
};
|
|
|
|
#define to_isa_dev(x) container_of((x), struct isa_dev, dev)
|
|
|
|
static int isa_bus_match(struct device *dev, struct device_driver *driver)
|
|
{
|
|
struct isa_driver *isa_driver = to_isa_driver(driver);
|
|
|
|
if (dev->platform_data == isa_driver) {
|
|
if (!isa_driver->match ||
|
|
isa_driver->match(dev, to_isa_dev(dev)->id))
|
|
return 1;
|
|
dev->platform_data = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int isa_bus_probe(struct device *dev)
|
|
{
|
|
struct isa_driver *isa_driver = dev->platform_data;
|
|
|
|
if (isa_driver->probe)
|
|
return isa_driver->probe(dev, to_isa_dev(dev)->id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isa_bus_remove(struct device *dev)
|
|
{
|
|
struct isa_driver *isa_driver = dev->platform_data;
|
|
|
|
if (isa_driver->remove)
|
|
return isa_driver->remove(dev, to_isa_dev(dev)->id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void isa_bus_shutdown(struct device *dev)
|
|
{
|
|
struct isa_driver *isa_driver = dev->platform_data;
|
|
|
|
if (isa_driver->shutdown)
|
|
isa_driver->shutdown(dev, to_isa_dev(dev)->id);
|
|
}
|
|
|
|
static int isa_bus_suspend(struct device *dev, pm_message_t state)
|
|
{
|
|
struct isa_driver *isa_driver = dev->platform_data;
|
|
|
|
if (isa_driver->suspend)
|
|
return isa_driver->suspend(dev, to_isa_dev(dev)->id, state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isa_bus_resume(struct device *dev)
|
|
{
|
|
struct isa_driver *isa_driver = dev->platform_data;
|
|
|
|
if (isa_driver->resume)
|
|
return isa_driver->resume(dev, to_isa_dev(dev)->id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct bus_type isa_bus_type = {
|
|
.name = "lpc-isa",
|
|
.match = isa_bus_match,
|
|
.probe = isa_bus_probe,
|
|
.remove = isa_bus_remove,
|
|
.shutdown = isa_bus_shutdown,
|
|
.suspend = isa_bus_suspend,
|
|
.resume = isa_bus_resume
|
|
};
|
|
|
|
static void isa_dev_release(struct device *dev)
|
|
{
|
|
kfree(to_isa_dev(dev));
|
|
}
|
|
|
|
void lpc_unregister_driver(struct isa_driver *isa_driver)
|
|
{
|
|
struct device *dev = isa_driver->devices;
|
|
|
|
while (dev) {
|
|
struct device *tmp = to_isa_dev(dev)->next;
|
|
device_unregister(dev);
|
|
dev = tmp;
|
|
}
|
|
driver_unregister(&isa_driver->driver);
|
|
}
|
|
|
|
|
|
int lpc_register_driver(struct isa_driver *isa_driver, unsigned int ndev)
|
|
{
|
|
int error;
|
|
unsigned int id;
|
|
|
|
isa_driver->driver.bus = &isa_bus_type;
|
|
isa_driver->devices = NULL;
|
|
|
|
error = driver_register(&isa_driver->driver);
|
|
if (error)
|
|
return error;
|
|
|
|
for (id = 0; id < ndev; id++) {
|
|
struct isa_dev *isa_dev;
|
|
|
|
isa_dev = kzalloc(sizeof *isa_dev, GFP_KERNEL);
|
|
if (!isa_dev) {
|
|
error = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
isa_dev->dev.parent = &isa_bus;
|
|
isa_dev->dev.bus = &isa_bus_type;
|
|
|
|
dev_set_name(&isa_dev->dev, "%s.%u",
|
|
isa_driver->driver.name, id);
|
|
isa_dev->dev.platform_data = isa_driver;
|
|
isa_dev->dev.release = isa_dev_release;
|
|
isa_dev->id = id;
|
|
|
|
isa_dev->dev.coherent_dma_mask = DMA_BIT_MASK(24);
|
|
isa_dev->dev.dma_mask = &isa_dev->dev.coherent_dma_mask;
|
|
|
|
error = device_register(&isa_dev->dev);
|
|
if (error) {
|
|
put_device(&isa_dev->dev);
|
|
break;
|
|
}
|
|
|
|
if (isa_dev->dev.platform_data) {
|
|
isa_dev->next = isa_driver->devices;
|
|
isa_driver->devices = &isa_dev->dev;
|
|
} else
|
|
device_unregister(&isa_dev->dev);
|
|
}
|
|
|
|
if (!error && !isa_driver->devices)
|
|
error = -ENODEV;
|
|
|
|
if (error)
|
|
isa_unregister_driver(isa_driver);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
int lpc_bus_init(void)
|
|
{
|
|
int error;
|
|
|
|
error = bus_register(&isa_bus_type);
|
|
if (!error) {
|
|
error = device_register(&isa_bus);
|
|
if (error)
|
|
bus_unregister(&isa_bus_type);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
void lpc_bus_exit(void)
|
|
{
|
|
|
|
device_unregister(&isa_bus);
|
|
|
|
bus_unregister(&isa_bus_type);
|
|
}
|
|
|
|
|
|
/********************************************** End ********************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/********************************************** Start ********************************************************/
|
|
/*
|
|
* module parameters:
|
|
*/
|
|
static int i2c_debug = 0;
|
|
static struct mutex lpc_lock;
|
|
|
|
|
|
#define DEB2(x) if (i2c_debug == 2) x
|
|
#define DEB3(x) if (i2c_debug == 3) x
|
|
/* print several statistical values */
|
|
#define DEBPROTO(x) if (i2c_debug == 9) x;
|
|
/* debug the protocol by showing transferred bits */
|
|
#define DEF_TIMEOUT 160
|
|
|
|
|
|
|
|
/* setting states on the bus with the right timing: */
|
|
|
|
#define set_lpc(adap, ctl, val) adap->setlpc(adap->data, ctl, val)
|
|
#define get_lpc(adap, ctl) adap->getlpc(adap->data, ctl)
|
|
#define get_own(adap) adap->getown(adap->data)
|
|
#define get_clock(adap) adap->getclock(adap->data)
|
|
#define i2c_outaddr(adap, val) adap->setlpc(adap->data, I2C_LPC_REG_DEVICE_ADDR, val)
|
|
|
|
#define i2c_outbyte1(adap, val) adap->setlpc(adap->data, I2C_LPC_REG_DATA_TX1, val)
|
|
#define i2c_outbyte2(adap, val) adap->setlpc(adap->data, I2C_LPC_REG_DATA_TX2, val)
|
|
#define i2c_outbyte3(adap, val) adap->setlpc(adap->data, I2C_LPC_REG_DATA_TX3, val)
|
|
#define i2c_outbyte4(adap, val) adap->setlpc(adap->data, I2C_LPC_REG_DATA_TX4, val)
|
|
#define i2c_inbyte1(adap) adap->getlpc(adap->data, I2C_LPC_REG_DATA_RX1)
|
|
#define i2c_inbyte2(adap) adap->getlpc(adap->data, I2C_LPC_REG_DATA_RX2)
|
|
#define i2c_inbyte3(adap) adap->getlpc(adap->data, I2C_LPC_REG_DATA_RX3)
|
|
#define i2c_inbyte4(adap) adap->getlpc(adap->data, I2C_LPC_REG_DATA_RX4)
|
|
|
|
|
|
|
|
#define LPC_FPRINTF_LOG_PATH "/tmp/file.log"
|
|
struct file *lpc_fprintf_file = NULL;
|
|
|
|
static int lpc_fprintf_debug(const char *fmt, ...)
|
|
{
|
|
char lpc_fprintf_buf[256]={0};
|
|
struct va_format vaf;
|
|
va_list args;
|
|
int r;
|
|
mm_segment_t old_fs;
|
|
struct timeval tv;
|
|
struct rtc_time tm;
|
|
|
|
do_gettimeofday(&tv);
|
|
|
|
rtc_time_to_tm(tv.tv_sec,&tm);
|
|
|
|
va_start(args, fmt);
|
|
vaf.fmt = fmt;
|
|
vaf.va = &args;
|
|
r=snprintf(lpc_fprintf_buf,sizeof(lpc_fprintf_buf),"[%04d.%08d] %pV\n",tm.tm_sec, (int)tv.tv_usec, &vaf);
|
|
va_end(args);
|
|
old_fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
vfs_write(lpc_fprintf_file, (char *)&lpc_fprintf_buf, strlen(lpc_fprintf_buf), &lpc_fprintf_file->f_pos);
|
|
set_fs(old_fs);
|
|
memset(lpc_fprintf_buf,0x0,sizeof(lpc_fprintf_buf));
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lpc_fprintf_init(void)
|
|
{
|
|
printk("lpc_fprintf_init.\n");
|
|
|
|
if(lpc_fprintf_file == NULL)
|
|
lpc_fprintf_file = filp_open(LPC_FPRINTF_LOG_PATH, O_RDWR | O_APPEND | O_CREAT, 0644);
|
|
|
|
if (IS_ERR(lpc_fprintf_file)) {
|
|
printk("Error occured while opening file %s, exiting...\n", LPC_FPRINTF_LOG_PATH);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lpc_fprintf_exit(void)
|
|
{
|
|
printk("lpc_fprintf_exit.\n");
|
|
|
|
if(lpc_fprintf_file != NULL)
|
|
filp_close(lpc_fprintf_file, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* other auxiliary functions */
|
|
|
|
|
|
void print_reg(struct i2c_algo_lpc_data *adap)
|
|
{
|
|
unsigned char status;
|
|
DEBPROTO(lpc_fprintf_debug("================================================\n");)
|
|
status = get_lpc(adap, I2C_LPC_REG_BUS_SEL);
|
|
DEBPROTO(lpc_fprintf_debug("%s select reg %x : %x\n",__func__,I2C_LPC_REG_BUS_SEL, status);)
|
|
status = get_lpc(adap, I2C_LPC_REG_BYTE_COUNT);
|
|
DEBPROTO(lpc_fprintf_debug("%s count reg %x : %x\n",__func__,I2C_LPC_REG_BYTE_COUNT, status);)
|
|
|
|
status = get_lpc(adap, I2C_LPC_REG_COMMAND);
|
|
DEBPROTO(lpc_fprintf_debug("%s command reg %x : %x\n",__func__,I2C_LPC_REG_COMMAND, status);)
|
|
status = get_lpc(adap, I2C_LPC_REG_DEVICE_ADDR);
|
|
DEBPROTO(lpc_fprintf_debug("%s address reg %x : %x\n",__func__,I2C_LPC_REG_DEVICE_ADDR, status);)
|
|
|
|
status = get_lpc(adap, I2C_LPC_REG_STATUS);
|
|
DEBPROTO(lpc_fprintf_debug("%s status reg %x : %x\n",__func__,I2C_LPC_REG_STATUS, status);)
|
|
}
|
|
|
|
|
|
|
|
static void i2c_repstart(struct i2c_algo_lpc_data *adap)
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("%s :\n",__func__);)
|
|
set_lpc(adap, I2C_LPC_REG_COMMAND, I2C_LPC_REPSTART);
|
|
}
|
|
|
|
static void i2c_stop(struct i2c_algo_lpc_data *adap)
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("%s :\n",__func__);)
|
|
set_lpc(adap, I2C_LPC_REG_COMMAND, I2C_LPC_STOP);
|
|
udelay(60);
|
|
set_lpc(adap, I2C_LPC_REG_COMMAND, 0x00);
|
|
}
|
|
|
|
|
|
|
|
|
|
static void i2c_start(struct i2c_algo_lpc_data *adap)
|
|
{
|
|
print_reg(adap);
|
|
|
|
set_lpc(adap, I2C_LPC_REG_COMMAND, I2C_LPC_START);
|
|
|
|
print_reg(adap);
|
|
}
|
|
|
|
|
|
|
|
|
|
static int wait_for_bb(struct i2c_algo_lpc_data *adap)
|
|
{
|
|
|
|
int timeout = DEF_TIMEOUT;
|
|
int status;
|
|
|
|
while (--timeout) {
|
|
status = get_lpc(adap, I2C_LPC_REG_STATUS);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("%s : Waiting for bus free status : %x\n",__func__,status);)
|
|
|
|
if(status == I2C_LPC_TD)
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("%s : Bus is free status : %x\n",__func__,status);)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (timeout == 0) {
|
|
DEBPROTO(lpc_fprintf_debug("%s : Timeout for free busy status : %x\n",__func__,status);)
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wait_for_be(int mode,struct i2c_algo_lpc_data *adap)
|
|
{
|
|
|
|
int timeout = DEF_TIMEOUT;
|
|
unsigned char status;
|
|
|
|
|
|
while (--timeout) {
|
|
|
|
status = get_lpc(adap, I2C_LPC_REG_STATUS);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("%s : Waiting for bus empty status : %x\n",__func__,status);)
|
|
|
|
if(mode == 1)
|
|
{
|
|
if((status & I2C_LPC_IBB) && (status & I2C_LPC_TBE))
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("%s : Bus is empty status : %x\n",__func__,status);)
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(status & I2C_LPC_TD)
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("%s : Bus is empty status : %x\n",__func__,status);)
|
|
break;
|
|
}
|
|
}
|
|
|
|
status = get_lpc(adap, I2C_LPC_REG_TEST);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("%s : The test register data : %x\n",__func__,status);)
|
|
udelay(1); /* wait for 100 us */
|
|
}
|
|
|
|
if (timeout == 0) {
|
|
DEBPROTO(lpc_fprintf_debug("%s : Timeout waiting for Bus Empty\n",__func__);)
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wait_for_bf(struct i2c_algo_lpc_data *adap)
|
|
{
|
|
|
|
int timeout = DEF_TIMEOUT;
|
|
int status;
|
|
|
|
|
|
while (--timeout) {
|
|
status = get_lpc(adap, I2C_LPC_REG_STATUS);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("%s : Waiting for bus full status : %x\n",__func__,status);)
|
|
|
|
if(status & I2C_LPC_RBF)
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("%s : Bus is full status : %x\n",__func__,status);)
|
|
break;
|
|
}
|
|
|
|
status = get_lpc(adap, I2C_LPC_REG_TEST);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("%s : The test register data : %x\n",__func__,status);)
|
|
udelay(1); /* wait for 100 us */
|
|
}
|
|
|
|
if (timeout == 0) {
|
|
DEBPROTO(lpc_fprintf_debug("%s : Timeout waiting for Bus Full\n",__func__);)
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wait_for_td(struct i2c_algo_lpc_data *adap)
|
|
{
|
|
|
|
int timeout = DEF_TIMEOUT;
|
|
int status=0;
|
|
|
|
while (--timeout) {
|
|
udelay(4);
|
|
status = get_lpc(adap, I2C_LPC_REG_STATUS);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("%s : Waiting for bus done status : %x\n",__func__,status);)
|
|
|
|
if(status == I2C_LPC_TD)
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("%s : Bus is done status : %x\n",__func__,status);)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (timeout == 0) {
|
|
DEBPROTO(lpc_fprintf_debug("%s : Timeout waiting for Bus Done\n",__func__);)
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int wait_for_pin(struct i2c_algo_lpc_data *adap, int *status)
|
|
{
|
|
int timeout = DEF_TIMEOUT;
|
|
*status = get_lpc(adap, I2C_LPC_REG_STATUS);
|
|
|
|
while ((*status & I2C_LPC_TBE) && --timeout) {
|
|
*status = get_lpc(adap, I2C_LPC_REG_STATUS);
|
|
}
|
|
|
|
if (timeout == 0)
|
|
return -ETIMEDOUT;
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int lpc_doAddress(struct i2c_algo_lpc_data *adap,struct i2c_msg *msg)
|
|
{
|
|
unsigned short flags = msg->flags;
|
|
unsigned char addr;
|
|
|
|
addr = msg->addr << 1;
|
|
if (flags & I2C_M_RD)
|
|
{
|
|
addr |= 1;
|
|
DEBPROTO(lpc_fprintf_debug("step 7 : read mode then write device address 0x%x\n",addr);)
|
|
}
|
|
else
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("step 2 : write mode then write device address 0x%x\n",addr);)
|
|
}
|
|
|
|
if (flags & I2C_M_REV_DIR_ADDR)
|
|
{
|
|
addr ^= 1;
|
|
|
|
}
|
|
i2c_outaddr(adap, addr);
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
static int lpc_sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
|
|
{
|
|
struct i2c_algo_lpc_data *adap = i2c_adap->algo_data;
|
|
int i = 0,timeout=0;
|
|
|
|
unsigned int count = msg->len;
|
|
unsigned char *buf = msg->buf;
|
|
|
|
do{
|
|
lpc_doAddress(adap,msg);
|
|
set_lpc(adap, I2C_LPC_REG_BYTE_COUNT, (count-i) >= 4 ? 4:(count - i));
|
|
DEBPROTO(lpc_fprintf_debug("step 3 : write register count %x\n",count);)
|
|
|
|
if((count -i) >= 4)
|
|
{
|
|
i2c_outbyte1(adap, buf[i+0] & 0xff);
|
|
i2c_outbyte2(adap, buf[i+1] & 0xff);
|
|
i2c_outbyte3(adap, buf[i+2] & 0xff);
|
|
i2c_outbyte4(adap, buf[i+3] & 0xff);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("step 4 : Send data[%d] = %x\n",i+0,buf[i+0]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 4 : Send data[%d] = %x\n",i+1,buf[i+1]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 4 : Send data[%d] = %x\n",i+2,buf[i+2]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 4 : Send data[%d] = %x\n",i+3,buf[i+3]);)
|
|
i += 4;
|
|
}
|
|
else if((count -i) == 3)
|
|
{
|
|
i2c_outbyte1(adap, buf[i+0] & 0xff);
|
|
i2c_outbyte2(adap, buf[i+1] & 0xff);
|
|
i2c_outbyte3(adap, buf[i+2] & 0xff);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("step 4 : Send data[%d] = %x\n",i+0,buf[i+0]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 4 : Send data[%d] = %x\n",i+1,buf[i+1]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 4 : Send data[%d] = %x\n",i+2,buf[i+2]);)
|
|
|
|
i += 3;
|
|
}
|
|
else if((count -i) == 2)
|
|
{
|
|
i2c_outbyte1(adap, buf[i+0] & 0xff);
|
|
i2c_outbyte2(adap, buf[i+1] & 0xff);
|
|
DEBPROTO(lpc_fprintf_debug("step 4 : Send data[%d] = %x\n",i+0,buf[i+0]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 4 : Send data[%d] = %x\n",i+1,buf[i+1]);)
|
|
i += 2;
|
|
}
|
|
else if((count -i) == 1)
|
|
{
|
|
i2c_outbyte1(adap, buf[i+0] & 0xff);
|
|
DEBPROTO(lpc_fprintf_debug("step 4 : Send data[%d] = %x\n",i+0,buf[i+0]);)
|
|
i += 1;
|
|
}
|
|
|
|
/* Send START */
|
|
DEBPROTO(lpc_fprintf_debug("step 5-1 : Delay 6mS \n");)
|
|
udelay(6000);
|
|
DEBPROTO(lpc_fprintf_debug("step 5-2 : Start to transfrom \n");)
|
|
i2c_stop(adap);
|
|
i2c_start(adap);
|
|
DEBPROTO(lpc_fprintf_debug("step 5-3 : Start done\n");)
|
|
|
|
udelay(400);
|
|
DEBPROTO(lpc_fprintf_debug("step 6 : Waiting for BE\n");)
|
|
timeout = wait_for_td(adap);
|
|
if (timeout) {
|
|
DEBPROTO(lpc_fprintf_debug("step 6 : Timeout waiting for BE \n");)
|
|
return -EREMOTEIO;
|
|
}
|
|
}while (i < count);
|
|
|
|
if(i == count)
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("Writen %d bytes successd !\n",count);)
|
|
return i;
|
|
}
|
|
else
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("Writen %d bytes failed \n",count);)
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
static int lpc_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
|
|
{
|
|
int i=0,timeout=0;
|
|
struct i2c_algo_lpc_data *adap = i2c_adap->algo_data;
|
|
|
|
unsigned int count = msg->len;
|
|
unsigned char *buf = msg->buf;
|
|
|
|
do{
|
|
lpc_doAddress(adap,msg);
|
|
set_lpc(adap, I2C_LPC_REG_BYTE_COUNT, (count-i) >= 4 ? 4:(count - i));
|
|
DEBPROTO(lpc_fprintf_debug("step 8 : write register count %d\n",count);)
|
|
|
|
/* Send START */
|
|
DEBPROTO(lpc_fprintf_debug("step 9-1 : Delay 6mS\n");)
|
|
udelay(6000);
|
|
DEBPROTO(lpc_fprintf_debug("step 9-2 : Start to receive data\n");)
|
|
i2c_stop(adap);
|
|
i2c_start(adap);
|
|
DEBPROTO(lpc_fprintf_debug("step 9-3 : Start done\n");)
|
|
|
|
udelay(400);
|
|
DEBPROTO(lpc_fprintf_debug("step 10 : Waiting for TD\n");)
|
|
timeout = wait_for_td(adap);
|
|
if (timeout) {
|
|
DEBPROTO(lpc_fprintf_debug("step 10 : Timeout waiting for TD \n");)
|
|
return -EREMOTEIO;
|
|
}
|
|
|
|
if((count -i) >= 4)
|
|
{
|
|
buf[i+0] = 0xff & i2c_inbyte1(adap);
|
|
buf[i+1] = 0xff & i2c_inbyte2(adap);
|
|
buf[i+2] = 0xff & i2c_inbyte3(adap);
|
|
buf[i+3] = 0xff & i2c_inbyte4(adap);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("step 11 : Receive data[%d] = %x\n",i+0,buf[i+0]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 11 : Receive data[%d] = %x\n",i+1,buf[i+1]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 11 : Receive data[%d] = %x\n",i+2,buf[i+2]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 11 : Receive data[%d] = %x\n",i+3,buf[i+3]);)
|
|
|
|
i += 4;
|
|
}
|
|
else if((count -i) == 3)
|
|
{
|
|
buf[i+0] = 0xff & i2c_inbyte1(adap);
|
|
buf[i+1] = 0xff & i2c_inbyte2(adap);
|
|
buf[i+2] = 0xff & i2c_inbyte3(adap);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("step 11 : Receive data[%d] = %x\n",i+0,buf[i+0]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 11 : Receive data[%d] = %x\n",i+1,buf[i+1]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 11 : Receive data[%d] = %x\n",i+2,buf[i+2]);)
|
|
|
|
i += 3;
|
|
}
|
|
else if((count -i) == 2)
|
|
{
|
|
buf[i+0] = 0xff & i2c_inbyte1(adap);
|
|
buf[i+1] = 0xff & i2c_inbyte2(adap);
|
|
DEBPROTO(lpc_fprintf_debug("step 11 : Receive data[%d] = %x\n",i+0,buf[i+0]);)
|
|
DEBPROTO(lpc_fprintf_debug("step 11 : Receive data[%d] = %x\n",i+1,buf[i+1]);)
|
|
i += 2;
|
|
}
|
|
else if((count -i) == 1)
|
|
{
|
|
buf[i+0] = 0xff & i2c_inbyte1(adap);
|
|
DEBPROTO(lpc_fprintf_debug("step 11 : Receive data[%d] = %x\n",i+0,buf[i+0]);)
|
|
i += 1;
|
|
}
|
|
|
|
|
|
}while(i < count);
|
|
|
|
if(i == count)
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("Read %d bytes successd !\n",count);)
|
|
return i;
|
|
}
|
|
else
|
|
{
|
|
DEBPROTO(lpc_fprintf_debug("Read %d bytes failed \n",count);)
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
|
|
struct cpld_client_node {
|
|
struct i2c_client *client;
|
|
struct list_head list;
|
|
};
|
|
#define LPC_I2C_MAX_NCHANS 6
|
|
|
|
|
|
struct lpc_iic {
|
|
struct i2c_adapter *virt_adaps[LPC_I2C_MAX_NCHANS];
|
|
u8 last_chan; /* last register value */
|
|
};
|
|
|
|
|
|
static int lpc_master_xfer(struct i2c_adapter *i2c_adap,
|
|
struct i2c_msg *msgs,
|
|
int num)
|
|
{
|
|
struct i2c_algo_lpc_data *adap = i2c_adap->algo_data;
|
|
struct i2c_msg *pmsg;
|
|
int i;
|
|
int ret=0;
|
|
|
|
mutex_lock(&lpc_lock);
|
|
|
|
if (adap->xfer_begin)
|
|
adap->xfer_begin(&i2c_adap->nr);
|
|
|
|
|
|
for (i = 0;ret >= 0 && i < num; i++) {
|
|
pmsg = &msgs[i];
|
|
|
|
DEBPROTO(lpc_fprintf_debug("lpc_xfer.o: Doing %s %d bytes to 0x%02x - %d of %d messages\n",
|
|
pmsg->flags & I2C_M_RD ? "read" : "write",
|
|
pmsg->len, pmsg->addr, i + 1, num);)
|
|
|
|
DEBPROTO(lpc_fprintf_debug("lpc_xfer.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n",
|
|
i, msgs[i].addr, msgs[i].flags, msgs[i].len);)
|
|
|
|
if (pmsg->flags & I2C_M_RD) {
|
|
ret = lpc_readbytes(i2c_adap, pmsg);
|
|
|
|
if (ret != pmsg->len) {
|
|
DEBPROTO(lpc_fprintf_debug("lpc_xfer.o: fail: "
|
|
"only read %d bytes.\n",ret));
|
|
} else {
|
|
DEBPROTO(lpc_fprintf_debug("lpc_xfer.o: read %d bytes.\n",ret));
|
|
}
|
|
} else {
|
|
|
|
ret = lpc_sendbytes(i2c_adap, pmsg);
|
|
|
|
if (ret != pmsg->len) {
|
|
DEBPROTO(lpc_fprintf_debug("lpc_xfer.o: fail: "
|
|
"only wrote %d bytes.\n",ret));
|
|
} else {
|
|
DEBPROTO(lpc_fprintf_debug("lpc_xfer.o: wrote %d bytes.\n",ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (adap->xfer_end)
|
|
adap->xfer_end(&i2c_adap->nr);
|
|
|
|
mutex_unlock(&lpc_lock);
|
|
|
|
DEBPROTO(lpc_fprintf_debug("ret = 0x%x num = 0x%x i = 0x%x.\n",ret,num,i));
|
|
|
|
return ret = (ret < 0) ? ret : num;
|
|
}
|
|
|
|
|
|
static u32 lpc_func(struct i2c_adapter *adap)
|
|
{
|
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_QUICK;
|
|
}
|
|
|
|
/* exported algorithm data: */
|
|
static const struct i2c_algorithm lpc_algo = {
|
|
.master_xfer = lpc_master_xfer,
|
|
//.smbus_xfer = lpc_smbus_xfer,
|
|
.functionality = lpc_func,
|
|
};
|
|
|
|
|
|
/********************************************** End ********************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/********************************************** Start ********************************************************/
|
|
#define DEFAULT_BASE 0x0a00
|
|
|
|
static int lpc_base= 0x0a00;
|
|
static u8 __iomem *lpc_base_iomem;
|
|
|
|
static int lpc_irq;
|
|
static int lpc_clock = 0x1c;
|
|
static int lpc_own = 0x55;
|
|
static int lpc_mmapped;
|
|
|
|
static unsigned long lpc_base_addr = 0x0a00;
|
|
static unsigned int lpc_io_space_size = 2;
|
|
|
|
static unsigned long LPC_INDEX_REG;
|
|
static unsigned long LPC_DATA_REG;
|
|
|
|
|
|
/* notice : removed static struct i2c_lpc_iic gpi; code -
|
|
this module in real supports only one device, due to missing arguments
|
|
in some functions, called from the algo-lpc module. Sometimes it's
|
|
need to be rewriten - but for now just remove this for simpler reading */
|
|
|
|
static wait_queue_head_t lpc_wait;
|
|
static int lpc_pending;
|
|
static spinlock_t lock;
|
|
static spinlock_t lpc_slock;
|
|
|
|
static struct i2c_adapter lpc_iic_ops;
|
|
|
|
struct cpld_dev_type {
|
|
struct resource *io_resource;
|
|
struct semaphore sem;
|
|
struct cdev cdev;
|
|
};
|
|
|
|
struct cpld_dev_type *cpld_device;
|
|
|
|
|
|
/* ----- local functions ---------------------------------------------- */
|
|
|
|
static void lpc_cpld_setbyte(void *data, int ctl, int val)
|
|
{
|
|
outb(ctl, LPC_INDEX_REG);
|
|
mb();
|
|
|
|
outb(val, LPC_DATA_REG);
|
|
mb();
|
|
}
|
|
|
|
static int lpc_cpld_getbyte(void *data, int ctl)
|
|
{
|
|
u8 val = 0;
|
|
|
|
outb(ctl, LPC_INDEX_REG);
|
|
mb();
|
|
|
|
val = inb(LPC_DATA_REG);
|
|
mb();
|
|
|
|
return val;
|
|
}
|
|
|
|
static void lpc_iic_setbyte(void *data, int ctl, int val)
|
|
{
|
|
if (!cpld_device)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
if (down_interruptible(&cpld_device->sem))
|
|
{
|
|
return ;
|
|
}
|
|
|
|
|
|
lpc_cpld_setbyte(data,ctl,val);
|
|
|
|
up(&cpld_device->sem);
|
|
DEBPROTO(lpc_fprintf_debug("%s REG[%x] = %x\n",__func__,ctl,val);)
|
|
}
|
|
|
|
|
|
static int lpc_iic_getbyte(void *data, int ctl)
|
|
{
|
|
u8 val = 0;
|
|
if (!cpld_device)
|
|
return -ENOTTY;
|
|
|
|
if (down_interruptible(&cpld_device->sem))
|
|
return -ERESTARTSYS;
|
|
|
|
val = lpc_cpld_getbyte(data,ctl);
|
|
|
|
up(&cpld_device->sem);
|
|
DEBPROTO(lpc_fprintf_debug("%s REG[%x] = %x\n",__func__,ctl,val);)
|
|
return val;
|
|
}
|
|
|
|
int cig_cpld_read_register(u8 reg_off, u8 *val)
|
|
{
|
|
if (!cpld_device)
|
|
return -ENOTTY;
|
|
|
|
if (down_interruptible(&cpld_device->sem))
|
|
return -ERESTARTSYS;
|
|
|
|
*val = lpc_cpld_getbyte(cpld_device, reg_off);
|
|
|
|
up(&cpld_device->sem);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(cig_cpld_read_register);
|
|
|
|
int cig_cpld_write_register(u8 reg_off, u8 val)
|
|
{
|
|
if (!cpld_device)
|
|
return -ENOTTY;
|
|
|
|
if (down_interruptible(&cpld_device->sem))
|
|
return -ERESTARTSYS;
|
|
|
|
lpc_cpld_setbyte(cpld_device, reg_off, val);
|
|
up(&cpld_device->sem);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(cig_cpld_write_register);
|
|
|
|
|
|
|
|
static int lpc_iic_getown(void *data)
|
|
{
|
|
return (lpc_own);
|
|
}
|
|
|
|
|
|
static int lpc_iic_getclock(void *data)
|
|
{
|
|
return (lpc_clock);
|
|
}
|
|
|
|
static void lpc_iic_waitforpin(void *data)
|
|
{
|
|
DEFINE_WAIT(wait);
|
|
int timeout = 2;
|
|
unsigned long flags;
|
|
|
|
if (lpc_irq > 0) {
|
|
spin_lock_irqsave(&lock, flags);
|
|
if (lpc_pending == 0) {
|
|
spin_unlock_irqrestore(&lock, flags);
|
|
prepare_to_wait(&lpc_wait, &wait, TASK_INTERRUPTIBLE);
|
|
if (schedule_timeout(timeout*HZ)) {
|
|
spin_lock_irqsave(&lock, flags);
|
|
if (lpc_pending == 1) {
|
|
lpc_pending = 0;
|
|
}
|
|
spin_unlock_irqrestore(&lock, flags);
|
|
}
|
|
finish_wait(&lpc_wait, &wait);
|
|
} else {
|
|
lpc_pending = 0;
|
|
spin_unlock_irqrestore(&lock, flags);
|
|
}
|
|
} else {
|
|
udelay(100);
|
|
}
|
|
}
|
|
|
|
|
|
static irqreturn_t lpc_iic_handler(int this_irq, void *dev_id) {
|
|
spin_lock(&lock);
|
|
lpc_pending = 1;
|
|
spin_unlock(&lock);
|
|
wake_up_interruptible(&lpc_wait);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int board_id = 0;
|
|
|
|
|
|
static int lpc_iic_select(void *data)
|
|
{
|
|
unsigned int chan_id=0;
|
|
chan_id = *(unsigned int *)data;
|
|
chan_id -= 2;
|
|
DEBPROTO(lpc_fprintf_debug("step 1 : selest channel id = %d\n",chan_id);)
|
|
lpc_iic_setbyte(data,I2C_LPC_REG_BUS_SEL,chan_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lpc_iic_deselect(void *data)
|
|
{
|
|
|
|
unsigned int chan_id=0;
|
|
chan_id = *(unsigned int *)data;
|
|
chan_id -= 2;
|
|
DEBPROTO(lpc_fprintf_debug("step last :deselect channel id = %d\n",chan_id);)
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
* Encapsulate the above functions in the correct operations structure.
|
|
* This is only done when more than one hardware adapter is supported.
|
|
*/
|
|
static struct i2c_algo_lpc_data lpc_iic_data = {
|
|
.setlpc = lpc_iic_setbyte,
|
|
.getlpc = lpc_iic_getbyte,
|
|
.getown = lpc_iic_getown,
|
|
.getclock = lpc_iic_getclock,
|
|
.waitforpin = lpc_iic_waitforpin,
|
|
.xfer_begin = lpc_iic_select,
|
|
.xfer_end = lpc_iic_deselect,
|
|
};
|
|
|
|
#include <linux/i2c-algo-bit.h>
|
|
|
|
static struct i2c_adapter lpc_iic_arr_ops[LPC_I2C_MAX_NCHANS] = {0};
|
|
|
|
static void dummy_setscl(void *data, int state)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static void dummy_setsda(void *data, int state)
|
|
{
|
|
return;
|
|
|
|
}
|
|
|
|
static int dummy_getscl(void *data)
|
|
{
|
|
return 1;
|
|
|
|
}
|
|
|
|
static int dummy_getsda(void *data)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
static struct i2c_algo_bit_data dummy_algo_data = {
|
|
.setsda = dummy_setsda,
|
|
.setscl = dummy_setscl,
|
|
.getsda = dummy_getsda,
|
|
.getscl = dummy_getscl,
|
|
.udelay = 50,
|
|
.timeout = HZ,
|
|
};
|
|
|
|
|
|
static int dummy_xfer(struct i2c_adapter *i2c_adap,
|
|
struct i2c_msg *msgs,
|
|
int num)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static u32 dummy_func(struct i2c_adapter *adap)
|
|
{
|
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_QUICK;
|
|
}
|
|
|
|
|
|
static const struct i2c_algorithm dummy_algo = {
|
|
.master_xfer = dummy_xfer,
|
|
.functionality = dummy_func,
|
|
};
|
|
|
|
|
|
|
|
static struct i2c_adapter i2c_dummy = {
|
|
.owner = THIS_MODULE,
|
|
.class = I2C_CLASS_HWMON,
|
|
.algo_data = &dummy_algo_data,
|
|
.algo = &dummy_algo,
|
|
.name = "i2c_dummy",
|
|
};
|
|
|
|
|
|
static int lpc_iic_match(struct device *dev, unsigned int id)
|
|
{
|
|
/* sanity checks for lpc_mmapped I/O */
|
|
|
|
DEB2(printk("lpc_iic_match\n");)
|
|
|
|
|
|
if (lpc_base < DEFAULT_BASE) {
|
|
dev_err(dev, "incorrect lpc_base address (%#x) specified "
|
|
"for lpc_mmapped I/O\n", lpc_base);
|
|
return 0;
|
|
}
|
|
|
|
if (lpc_base == 0) {
|
|
lpc_base = DEFAULT_BASE;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int lpc_iic_probe(struct device *dev, unsigned int id)
|
|
{
|
|
int rval,num;
|
|
|
|
lpc_fprintf_init();
|
|
|
|
DEB2(printk("lpc_iic_probe\n");)
|
|
|
|
mutex_init(&lpc_lock);
|
|
|
|
for(num = 0; num < LPC_I2C_MAX_NCHANS;num++)
|
|
{
|
|
lpc_iic_arr_ops[num].dev.parent = dev;
|
|
lpc_iic_arr_ops[num].owner = THIS_MODULE;
|
|
lpc_iic_arr_ops[num].class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
|
lpc_iic_arr_ops[num].algo = &lpc_algo;
|
|
lpc_iic_arr_ops[num].algo_data = &lpc_iic_data,
|
|
lpc_iic_arr_ops[num].nr=num;
|
|
snprintf(lpc_iic_arr_ops[num].name, sizeof(lpc_iic_arr_ops[num].name), "i2c-%d-lpc", i2c_adapter_id(&lpc_iic_arr_ops[num]));
|
|
rval |= i2c_add_adapter(&lpc_iic_arr_ops[num]);
|
|
DEB2(printk("%s\n",lpc_iic_arr_ops[num].name);)
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lpc_iic_remove(struct device *dev, unsigned int id)
|
|
{
|
|
int num;
|
|
DEB2(printk("lpc_iic_remove\n"));
|
|
|
|
lpc_fprintf_exit();
|
|
for(num = LPC_I2C_MAX_NCHANS - 1; num >= 0 ;num--)
|
|
i2c_del_adapter(&lpc_iic_arr_ops[num]);
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct isa_driver i2c_lpc_driver = {
|
|
.match = lpc_iic_match,
|
|
.probe = lpc_iic_probe,
|
|
.remove = lpc_iic_remove,
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = "lpc-iic",
|
|
},
|
|
};
|
|
|
|
/********************************************** End ********************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/********************************************** Start ********************************************************/
|
|
|
|
static int cpld_major = 0;
|
|
static int cpld_minor = 0;
|
|
|
|
struct cpld_rw_msg {
|
|
unsigned char addr;
|
|
unsigned char data;
|
|
};
|
|
|
|
|
|
static struct cpld_rw_msg param_read = {-1};
|
|
static struct cpld_rw_msg param_write = {-1};
|
|
static struct cpld_rw_msg param_reads = {-1};
|
|
static struct cpld_rw_msg param_writes = {-1};
|
|
|
|
void cpld_sysfs_kobj_release(struct kobject *kobj)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int cpld_sysfs_add_attr(struct kobject* kobj, char* attr_name)
|
|
{
|
|
|
|
struct attribute *attr;
|
|
|
|
attr = kmalloc(sizeof(struct attribute), GFP_KERNEL);
|
|
attr->name = attr_name;
|
|
attr->mode = 0644;
|
|
|
|
return sysfs_create_file(kobj, attr);
|
|
}
|
|
|
|
static int cig_cpld_write_slave_cpld_register(u8 reg_addr, u8 reg_data);
|
|
static int cig_cpld_read_slave_cpld_register(u8 reg_addr, u8 *reg_data);
|
|
|
|
|
|
static ssize_t cpld_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buffer)
|
|
{
|
|
u8 val=0,ret=0,year=0,month=0,day=0,cpld_m=0,cpld_1=0,cpld_2=0;
|
|
|
|
if (0 == strcmp(attr->name, "read"))
|
|
{
|
|
val = lpc_iic_getbyte(NULL,param_read.addr);
|
|
ret = sprintf(buffer,"read : addr = 0x%x val = 0x%x\n",param_read.addr, val);
|
|
|
|
}
|
|
else if (0 == strcmp(attr->name, "write"))
|
|
{
|
|
lpc_iic_setbyte(NULL, param_write.addr,param_write.data);
|
|
ret = sprintf(buffer,"write : addr = 0x%x val = 0x%x\n",param_write.addr, param_write.data);
|
|
}
|
|
else if (0 == strcmp(attr->name, "version"))
|
|
{
|
|
cpld_m = lpc_iic_getbyte(NULL, 0x02);
|
|
year = lpc_iic_getbyte(NULL, 0x03);
|
|
month = lpc_iic_getbyte(NULL, 0x04);
|
|
day = lpc_iic_getbyte(NULL, 0x05);
|
|
|
|
cig_cpld_read_slave_cpld_register(0x1d,&cpld_1);
|
|
cig_cpld_read_slave_cpld_register(0x1e,&cpld_2);
|
|
|
|
ret = sprintf(buffer,"Main CPLD version : V%02x\n"\
|
|
"Main CPLD date : 20%02x-%02x-%02x\n"\
|
|
"Slave 1 CPLD version : V%02x\n"\
|
|
"Slave 2 CPLD version : V%02x\n",cpld_m,year,month,day,cpld_1,cpld_2);
|
|
}
|
|
if (0 == strcmp(attr->name, "reads"))
|
|
{
|
|
ret = cig_cpld_read_slave_cpld_register(param_reads.addr,&val);
|
|
if (ret < 0)
|
|
printk("ERROR:Failed to read slave cpld.\n");
|
|
ret = sprintf(buffer,"reads : addr = 0x%x val = 0x%x\n",param_reads.addr, val);
|
|
|
|
}
|
|
else if (0 == strcmp(attr->name, "writes"))
|
|
{
|
|
ret = cig_cpld_write_slave_cpld_register(param_writes.addr,param_writes.data);
|
|
if (ret < 0)
|
|
printk("ERROR:Failed to read slave cpld.\n");
|
|
ret = sprintf(buffer,"writes : addr = 0x%x val = 0x%x\n",param_writes.addr, param_writes.data);
|
|
}
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
static ssize_t cpld_sysfs_store(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t count)
|
|
{
|
|
int param[3];
|
|
|
|
if (0 == strcmp(attr->name, "read"))
|
|
{
|
|
sscanf(buffer, "0x%02x", ¶m[0]);
|
|
param_read.addr = param[0];
|
|
}
|
|
else if (0 == strcmp(attr->name, "write"))
|
|
{
|
|
sscanf(buffer, "0x%2x 0x%02x", ¶m[0], ¶m[1]);
|
|
param_write.addr = param[0];
|
|
param_write.data = param[1];
|
|
}
|
|
if (0 == strcmp(attr->name, "reads"))
|
|
{
|
|
sscanf(buffer, "0x%02x", ¶m[0]);
|
|
param_reads.addr = param[0];
|
|
}
|
|
else if (0 == strcmp(attr->name, "writes"))
|
|
{
|
|
sscanf(buffer, "0x%2x 0x%02x", ¶m[0], ¶m[1]);
|
|
param_writes.addr = param[0];
|
|
param_writes.data = param[1];
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
|
|
static struct sysfs_ops cpld_sysfs_ops =
|
|
{
|
|
.show = cpld_sysfs_show,
|
|
.store = cpld_sysfs_store,
|
|
};
|
|
|
|
static struct kobj_type cpld_kobj_type =
|
|
{
|
|
.release = cpld_sysfs_kobj_release,
|
|
.sysfs_ops = &cpld_sysfs_ops,
|
|
.default_attrs = NULL,
|
|
};
|
|
|
|
|
|
static const char driver_name[] = "cpld_drv";
|
|
static atomic_t cpld_available = ATOMIC_INIT(1);
|
|
static struct class *cpld_class;
|
|
static struct device *cpld_dev;
|
|
|
|
|
|
|
|
#define CPLD_IOC_MAGIC '['
|
|
|
|
#define CPLD_IOC_RDREG _IOR(CPLD_IOC_MAGIC, 0, struct cpld_rw_msg)
|
|
#define CPLD_IOC_WRREG _IOW(CPLD_IOC_MAGIC, 1, struct cpld_rw_msg)
|
|
|
|
#define CPLD_IOC_MAXNR 2
|
|
|
|
|
|
int cpld_open(struct inode *inode, struct file *filp)
|
|
{
|
|
struct cpld_dev_type *dev;
|
|
|
|
if (! atomic_dec_and_test(&cpld_available)) {
|
|
atomic_inc(&cpld_available);
|
|
return -EBUSY;
|
|
}
|
|
|
|
dev = container_of(inode->i_cdev, struct cpld_dev_type, cdev);
|
|
filp->private_data = dev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cpld_release(struct inode *inode, struct file *flip)
|
|
{
|
|
atomic_inc(&cpld_available);
|
|
return 0;
|
|
}
|
|
|
|
|
|
long cpld_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int rc = 0;
|
|
int err = 0;
|
|
struct cpld_dev_type *dev = (struct cpld_dev_type *)filp->private_data;
|
|
struct cpld_rw_msg msg;
|
|
|
|
if (_IOC_TYPE(cmd) != CPLD_IOC_MAGIC)
|
|
return -ENOTTY;
|
|
if (_IOC_NR(cmd) > CPLD_IOC_MAXNR)
|
|
return -ENOTTY;
|
|
|
|
if (_IOC_DIR(cmd) & _IOC_READ)
|
|
err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
|
|
if (_IOC_DIR(cmd) & _IOC_WRITE)
|
|
err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
|
|
if (err)
|
|
return -EFAULT;
|
|
|
|
if (down_interruptible(&dev->sem))
|
|
return -ERESTARTSYS;
|
|
|
|
switch(cmd){
|
|
case CPLD_IOC_RDREG:
|
|
rc = copy_from_user(&msg, (void __user *)arg, sizeof(msg));
|
|
if (!rc) {
|
|
msg.data = lpc_cpld_getbyte(dev, msg.addr);
|
|
rc = copy_to_user((void __user *)arg, &msg, sizeof(msg));
|
|
}
|
|
break;
|
|
|
|
case CPLD_IOC_WRREG:
|
|
rc = copy_from_user(&msg, (void __user *)arg, sizeof(msg));
|
|
if (!rc) {
|
|
lpc_cpld_setbyte(dev, msg.addr, msg.data);
|
|
}
|
|
break;
|
|
default:
|
|
rc = -ENOTTY;
|
|
break;
|
|
}
|
|
up(&dev->sem);
|
|
|
|
return rc;
|
|
}
|
|
|
|
struct file_operations cpld_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = cpld_open,
|
|
.unlocked_ioctl = cpld_ioctl,
|
|
.release = cpld_release,
|
|
};
|
|
|
|
|
|
static void cpld_setup_cdev(struct cpld_dev_type *dev)
|
|
{
|
|
int err, devno = MKDEV(cpld_major, cpld_minor);
|
|
|
|
cdev_init(&dev->cdev, &cpld_fops);
|
|
dev->cdev.owner = THIS_MODULE;
|
|
dev->cdev.ops = &cpld_fops;
|
|
err = cdev_add(&dev->cdev, devno, 1);
|
|
|
|
if (err)
|
|
DEB2(printk(KERN_NOTICE "Error %d adding cpld", err);)
|
|
}
|
|
/********************************************** End ********************************************************/
|
|
|
|
|
|
|
|
|
|
/********************************************** Start ********************************************************/
|
|
#include <linux/interrupt.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/irq.h>
|
|
|
|
static spinlock_t irq_inter_lock;
|
|
static struct delayed_work irq_inter_work;
|
|
static unsigned long irq_inter_delay;
|
|
|
|
|
|
static int cig_cpld_write_slave_cpld_register(u8 reg_addr, u8 reg_data)
|
|
{
|
|
u8 read_status = 0;
|
|
u8 wait_time_out = WAIT_TIME_OUT_COUNT;
|
|
DEB2(printk("<=======write=========>"));
|
|
cig_cpld_write_register(ADDR_REG_SFP_STATUS_ADDR, reg_addr << 1);
|
|
DEB2(printk("[62]=%x\n",reg_addr << 1));
|
|
cig_cpld_write_register(ADDR_REG_SFP_STATUS_TX, reg_data);
|
|
DEB2(printk("[63]=%x\n",reg_data));
|
|
cig_cpld_write_register(ADDR_REG_SFP_STATUS_COMMAND, 0x80);
|
|
DEB2(printk("[65]=%x\n",0x80));
|
|
do{
|
|
cig_cpld_read_register(ADDR_REG_SFP_STATUS_STATUS, &read_status);
|
|
DEB2(printk("[66]=%x\n",read_status));
|
|
udelay(60);
|
|
wait_time_out--;
|
|
if(wait_time_out == 0)
|
|
break;
|
|
}while(read_status != 0x02);
|
|
DEB2(printk("<=======write=========>"));
|
|
|
|
|
|
if(wait_time_out == 0)
|
|
return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int cig_cpld_read_slave_cpld_register(u8 reg_addr, u8 *reg_data)
|
|
{
|
|
u8 read_status = 0;
|
|
u8 wait_time_out = WAIT_TIME_OUT_COUNT;
|
|
DEB2(printk("<========read=========>"));
|
|
cig_cpld_write_register(ADDR_REG_SFP_STATUS_ADDR, reg_addr << 1 | 1);
|
|
DEB2(printk("[62]=%x\n",reg_addr << 1 | 1));
|
|
cig_cpld_write_register(ADDR_REG_SFP_STATUS_COMMAND, 0x80);
|
|
DEB2(printk("[65]=%x\n",0x80));
|
|
do{
|
|
udelay(60);
|
|
cig_cpld_read_register(ADDR_REG_SFP_STATUS_STATUS, &read_status);
|
|
DEB2(printk("[66]=%x\n",read_status));
|
|
wait_time_out--;
|
|
if(wait_time_out == 0)
|
|
break;
|
|
}while(read_status != 0x01);
|
|
|
|
cig_cpld_read_register(ADDR_REG_SFP_STATUS_RX,reg_data);
|
|
DEB2(printk("[64]=%x\n",*reg_data));
|
|
DEB2(printk("<========read=========>"));
|
|
|
|
if(wait_time_out == 0)
|
|
return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
struct sock *nlsk = NULL;
|
|
extern struct net init_net;
|
|
#define NETLINK_TEST 26
|
|
#define MSG_LEN 125
|
|
#define USER_PORT 100
|
|
static u32 irq_present_status_low_current,irq_present_status_low_next;
|
|
static u32 irq_present_status_high_current,irq_present_status_high_next;
|
|
static u32 irq_tx_fault_status_low_current,irq_tx_fault_status_low_next;
|
|
static u32 irq_tx_fault_status_high_current,irq_tx_fault_status_high_next;
|
|
static u32 irq_rx_lost_status_low_current,irq_rx_lost_status_low_next;
|
|
static u32 irq_rx_lost_status_high_current,irq_rx_lost_status_high_next;
|
|
|
|
static u8 irq_present_qsfp_current,irq_present_qsfp_next;
|
|
static u8 irq_interrupt_qsfp_current,irq_interrupt_qsfp_next;
|
|
|
|
struct input_dev *cpld_input_dev;
|
|
|
|
|
|
|
|
int send_usrmsg(char *pbuf, uint16_t len)
|
|
{
|
|
struct sk_buff *nl_skb;
|
|
struct nlmsghdr *nlh;
|
|
|
|
int ret;
|
|
|
|
|
|
nl_skb = nlmsg_new(len, GFP_ATOMIC);
|
|
if(!nl_skb)
|
|
{
|
|
printk("netlink alloc failure\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_TEST, len, 0);
|
|
if(nlh == NULL)
|
|
{
|
|
printk("nlmsg_put failaure \n");
|
|
nlmsg_free(nl_skb);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(nlmsg_data(nlh), pbuf, len);
|
|
ret = netlink_unicast(nlsk, nl_skb, USER_PORT, MSG_DONTWAIT);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void netlink_rcv_msg(struct sk_buff *skb)
|
|
{
|
|
|
|
struct nlmsghdr *nlh = NULL;
|
|
char *umsg = NULL;
|
|
char kmsg[1024] = {0};
|
|
char kmsg_tmp[16] = {0};
|
|
u8 i = 0;
|
|
u8 tmp[3]={0};
|
|
|
|
if(skb->len >= nlmsg_total_size(0))
|
|
{
|
|
nlh = nlmsg_hdr(skb);
|
|
umsg = NLMSG_DATA(nlh);
|
|
if(umsg)
|
|
{
|
|
for(i = 0;i < 24;i++)
|
|
{
|
|
if(!(irq_present_status_low_current & (0x1 << i)))
|
|
{
|
|
tmp[0] = 1;
|
|
}
|
|
else
|
|
{
|
|
tmp[0] = 0;
|
|
}
|
|
|
|
if(!(irq_rx_lost_status_low_current & (0x1 << i)))
|
|
{
|
|
tmp[1] = 1;
|
|
}
|
|
else
|
|
{
|
|
tmp[1] = 0;
|
|
}
|
|
|
|
if(!(irq_tx_fault_status_low_current & (0x1 << i)))
|
|
{
|
|
tmp[2] = 1;
|
|
}
|
|
else
|
|
{
|
|
tmp[2] = 0;
|
|
}
|
|
memset(kmsg_tmp,0xff,sizeof(kmsg_tmp));
|
|
sprintf(kmsg_tmp,"sfp%02d:%1d:%1d:%1d ",i+1,tmp[0],tmp[1],tmp[2]);
|
|
strcat(kmsg,kmsg_tmp);
|
|
}
|
|
|
|
for(i = 0;i < 24;i++)
|
|
{
|
|
if(!(irq_present_status_high_current & (0x1 << i)))
|
|
{
|
|
tmp[0] = 1;
|
|
}
|
|
else
|
|
{
|
|
tmp[0] = 0;
|
|
}
|
|
|
|
if(!(irq_rx_lost_status_high_current & (0x1 << i)))
|
|
{
|
|
tmp[1] = 1;
|
|
}
|
|
else
|
|
{
|
|
tmp[1] = 0;
|
|
}
|
|
|
|
if(!(irq_tx_fault_status_high_current & (0x1 << i)))
|
|
{
|
|
tmp[2] = 1;
|
|
}
|
|
else
|
|
{
|
|
tmp[2] = 0;
|
|
}
|
|
memset(kmsg_tmp,0xff,sizeof(kmsg_tmp));
|
|
sprintf(kmsg_tmp,"sfp%02d:%1d:%1d:%1d ",i+25,tmp[0],tmp[1],tmp[2]);
|
|
strcat(kmsg,kmsg_tmp);
|
|
}
|
|
|
|
|
|
for(i = 0;i < 8;i++)
|
|
{
|
|
if(!(irq_present_qsfp_current & (0x1 << i)))
|
|
{
|
|
tmp[0] = 1;
|
|
}
|
|
else
|
|
{
|
|
tmp[0] = 0;
|
|
}
|
|
|
|
if(!(irq_interrupt_qsfp_current & (0x1 << i)))
|
|
{
|
|
tmp[1] = 1;
|
|
}
|
|
else
|
|
{
|
|
tmp[1] = 0;
|
|
}
|
|
|
|
memset(kmsg_tmp,0xff,sizeof(kmsg_tmp));
|
|
sprintf(kmsg_tmp,"qsfp%02d:%1d:%1d:%1d ",i+49,tmp[0],tmp[1],0);
|
|
strcat(kmsg,kmsg_tmp);
|
|
}
|
|
|
|
printk("kernel recv from user: %s\n", umsg);
|
|
send_usrmsg(kmsg, strlen(kmsg));
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
|
|
struct netlink_kernel_cfg cfg = {
|
|
.input = netlink_rcv_msg, /* set recv callback */
|
|
};
|
|
|
|
|
|
|
|
#define RANGE_OF_BYTE_SHIFT(to_arg,shift,from_arg) {to_arg &= ~(0xff << shift); to_arg |= from_arg << shift;}
|
|
|
|
|
|
static void irq_inter_wapper(struct work_struct * work)
|
|
{
|
|
|
|
u8 m_data = 0;
|
|
u8 data_high8 = 0,data_low8 = 0;
|
|
u16 data_16 = 0;
|
|
u8 status = 0;
|
|
u8 i = 0;
|
|
char kmsg[64]={0};
|
|
u8 tmp[3] = {0};
|
|
|
|
DEB2(printk("CPLD_MASTER_INTERRUPT\r\n"));
|
|
|
|
m_data = lpc_iic_getbyte(NULL,CPLD_MASTER_INTERRUPT_STATUS_REG);
|
|
lpc_iic_setbyte(NULL,CPLD_MASTER_INTERRUPT_STATUS_REG,0xff);
|
|
|
|
cig_cpld_write_slave_cpld_register(CPLD_SLAVE1_INTERRUPT_MASK_REG,0xff);
|
|
cig_cpld_write_slave_cpld_register(CPLD_SLAVE2_INTERRUPT_MASK_REG,0xff);
|
|
if(!(m_data & CPLD_MASTER_INTERRUPT_CPLD1))
|
|
{
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_INTERRUPT_STATUS_H_REG,&data_high8);
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_INTERRUPT_STATUS_L_REG,&data_low8);
|
|
data_16 = data_low8 | data_high8 << 8;
|
|
if(
|
|
!(data_16 & CPLD_SLAVE1_INTERRUPT_PRESENT08) ||
|
|
!(data_16 & CPLD_SLAVE1_INTERRUPT_PRESENT16) ||
|
|
!(data_16 & CPLD_SLAVE1_INTERRUPT_PRESENT24)
|
|
)
|
|
{
|
|
if(!(data_16 & CPLD_SLAVE1_INTERRUPT_PRESENT08))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE1_INTERRUPT_PRESENT08\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_PRESENT08_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_present_status_low_current,0,status);
|
|
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE1_INTERRUPT_PRESENT16))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE1_INTERRUPT_PRESENT16\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_PRESENT16_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_present_status_low_current,8,status);
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE1_INTERRUPT_PRESENT24))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE1_INTERRUPT_PRESENT24\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_PRESENT24_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_present_status_low_current,16,status);
|
|
}
|
|
DEB2(printk("irq_present_status_low_next = %08x irq_present_status_low_current = %08x \n",irq_present_status_low_next,irq_present_status_low_current));
|
|
}
|
|
|
|
if(
|
|
!(data_16 & CPLD_SLAVE1_INTERRUPT_RX_LOST08) ||
|
|
!(data_16 & CPLD_SLAVE1_INTERRUPT_RX_LOST16) ||
|
|
!(data_16 & CPLD_SLAVE1_INTERRUPT_RX_LOST24)
|
|
)
|
|
{
|
|
if(!(data_16 & CPLD_SLAVE1_INTERRUPT_RX_LOST08))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE1_INTERRUPT_RX_LOST08\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_RX_LOST08_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_rx_lost_status_low_current,0,status);
|
|
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE1_INTERRUPT_RX_LOST16))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE1_INTERRUPT_PRESENT16\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_RX_LOST16_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_rx_lost_status_low_current,8,status);
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE1_INTERRUPT_RX_LOST24))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE1_INTERRUPT_PRESENT24\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_RX_LOST24_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_rx_lost_status_low_current,16,status);
|
|
}
|
|
DEB2(printk("irq_rx_lost_status_low_next = %08x irq_rx_lost_status_low_current = %08x \n",irq_rx_lost_status_low_next,irq_rx_lost_status_low_current));
|
|
}
|
|
|
|
if(
|
|
!(data_16 & CPLD_SLAVE1_INTERRUPT_RX_LOST08) ||
|
|
!(data_16 & CPLD_SLAVE1_INTERRUPT_RX_LOST16) ||
|
|
!(data_16 & CPLD_SLAVE1_INTERRUPT_RX_LOST24)
|
|
)
|
|
{
|
|
if(!(data_16 & CPLD_SLAVE1_INTERRUPT_TX_FAULT08))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE1_INTERRUPT_TX_FAULT08\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_TX_FAULT08_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_tx_fault_status_low_current,0,status);
|
|
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE1_INTERRUPT_TX_FAULT16))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE1_INTERRUPT_TX_FAULT16\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_TX_FAULT16_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_tx_fault_status_low_current,8,status);
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE1_INTERRUPT_TX_FAULT24))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE1_INTERRUPT_TX_FAULT24\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE1_TX_FAULT24_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_tx_fault_status_low_current,16,status);
|
|
}
|
|
DEB2(printk("irq_tx_fault_status_low_next = %08x irq_tx_fault_status_low_current = %08x \n",irq_tx_fault_status_low_next,irq_tx_fault_status_low_current));
|
|
|
|
}
|
|
}
|
|
else if(!(m_data & CPLD_MASTER_INTERRUPT_CPLD2))
|
|
{
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_INTERRUPT_STATUS_H_REG,&data_high8);
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_INTERRUPT_STATUS_L_REG,&data_low8);
|
|
data_16 = data_low8 | data_high8 << 8;
|
|
if(
|
|
!(data_16 & CPLD_SLAVE2_INTERRUPT_PRESENT32) ||
|
|
!(data_16 & CPLD_SLAVE2_INTERRUPT_PRESENT40) ||
|
|
!(data_16 & CPLD_SLAVE2_INTERRUPT_PRESENT48)
|
|
)
|
|
{
|
|
if(!(data_16 & CPLD_SLAVE2_INTERRUPT_PRESENT32))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_PRESENT32_REG\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_PRESENT32_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_present_status_high_current,0,status);
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE2_INTERRUPT_PRESENT40))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_PRESENT40_REG\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_PRESENT40_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_present_status_high_current,8,status);
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE2_INTERRUPT_PRESENT48))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_INTERRUPT_PRESENT48\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_PRESENT48_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_present_status_high_current,16,status);
|
|
}
|
|
}
|
|
|
|
if(
|
|
!(data_16 & CPLD_SLAVE2_INTERRUPT_RX_LOST32) ||
|
|
!(data_16 & CPLD_SLAVE2_INTERRUPT_RX_LOST40) ||
|
|
!(data_16 & CPLD_SLAVE2_INTERRUPT_RX_LOST48)
|
|
)
|
|
{
|
|
if(!(data_16 & CPLD_SLAVE2_INTERRUPT_RX_LOST32))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_INTERRUPT_RX_LOST32\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_RX_LOST32_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_rx_lost_status_high_current,0,status);
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE2_INTERRUPT_RX_LOST40))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_INTERRUPT_PRESENT40\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_RX_LOST40_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_rx_lost_status_high_current,8,status);
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE2_INTERRUPT_RX_LOST48))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_INTERRUPT_PRESENT48\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_RX_LOST48_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_rx_lost_status_high_current,16,status);
|
|
}
|
|
|
|
}
|
|
|
|
if(
|
|
!(data_16 & CPLD_SLAVE2_INTERRUPT_TX_FAULT32) ||
|
|
!(data_16 & CPLD_SLAVE2_INTERRUPT_TX_FAULT40) ||
|
|
!(data_16 & CPLD_SLAVE2_INTERRUPT_TX_FAULT48)
|
|
)
|
|
{
|
|
if(!(data_16 & CPLD_SLAVE2_INTERRUPT_TX_FAULT32))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_INTERRUPT_RX_LOST32\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_TX_FAULT32_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_tx_fault_status_high_current,0,status);
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE2_INTERRUPT_TX_FAULT40))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_INTERRUPT_PRESENT40\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_TX_FAULT40_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_tx_fault_status_high_current,8,status);
|
|
}
|
|
else if(!(data_16 & CPLD_SLAVE2_INTERRUPT_TX_FAULT48))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_INTERRUPT_PRESENT48\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_TX_FAULT48_REG,&status);
|
|
RANGE_OF_BYTE_SHIFT(irq_tx_fault_status_high_current,16,status);
|
|
}
|
|
}
|
|
|
|
if(!(data_16 & CPLD_SLAVE2_INTERRUPT_PRESENT56))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_PRESENT56_REG\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_PRESENT56_REG,&status);
|
|
irq_present_qsfp_current = status;
|
|
}
|
|
|
|
if(!(data_16 & CPLD_SLAVE2_INTERRUPT_QSFP_CR56))
|
|
{
|
|
DEB2(printk("CPLD_SLAVE2_QSFP_CR56_REG\r\n"));
|
|
cig_cpld_read_slave_cpld_register(CPLD_SLAVE2_QSFP_CR56_REG,&status);
|
|
irq_interrupt_qsfp_current = status;
|
|
}
|
|
}
|
|
else if(!(m_data & CPLD_MASTER_INTERRUPT_LSW))
|
|
{
|
|
DEB2(printk("CPLD_MASTER_INTERRUPT_LSW\r\n"));
|
|
}
|
|
else if(!(m_data & CPLD_MASTER_INTERRUPT_PSU1))
|
|
{
|
|
DEB2(printk("CPLD_MASTER_INTERRUPT_PSU1\r\n"));
|
|
}
|
|
else if(!(m_data & CPLD_MASTER_INTERRUPT_PSU2))
|
|
{
|
|
DEB2(printk("CPLD_MASTER_INTERRUPT_PSU2\r\n"));
|
|
}
|
|
else if(!(m_data & CPLD_MASTER_INTERRUPT_6320))
|
|
{
|
|
DEB2(printk("CPLD_MASTER_INTERRUPT_6320\r\n"));
|
|
}
|
|
cig_cpld_write_slave_cpld_register(CPLD_SLAVE1_INTERRUPT_MASK_REG,0x0);
|
|
cig_cpld_write_slave_cpld_register(CPLD_SLAVE2_INTERRUPT_MASK_REG,0x0);
|
|
|
|
memset(tmp,0xff,sizeof(tmp));
|
|
|
|
for(i = 0;i < 24;i++)
|
|
{
|
|
if(!(irq_present_status_low_current & (0x1 << i)) && (irq_present_status_low_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d is present\r\n",i+1));
|
|
tmp[0] = 1;
|
|
}
|
|
else if((irq_present_status_low_current & (0x1 << i)) && !(irq_present_status_low_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d is absent\r\n",i+1));
|
|
tmp[0] = 0;
|
|
}
|
|
|
|
if(!(irq_tx_fault_status_low_current & (0x1 << i)) && (irq_tx_fault_status_low_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d transmission is right\r\n",i+1));
|
|
tmp[1] = 1;
|
|
}
|
|
else if((irq_tx_fault_status_low_current & (0x1 << i)) && !(irq_tx_fault_status_low_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d transmission is fault\r\n",i+1));
|
|
tmp[1] = 0;
|
|
}
|
|
|
|
if(!(irq_rx_lost_status_low_current & (0x1 << i)) && (irq_rx_lost_status_low_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d optical is meet\r\n",i+1));
|
|
tmp[2] = 1;
|
|
}
|
|
else if((irq_rx_lost_status_low_current & (0x1 << i)) && !(irq_rx_lost_status_low_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d optical is lost\r\n",i+1));
|
|
tmp[2] = 0;
|
|
}
|
|
|
|
if((tmp[0] != 0xff) || (tmp[1] != 0xff) || (tmp[2] != 0xff))
|
|
{
|
|
memset(kmsg,0xff,sizeof(kmsg));
|
|
snprintf(kmsg,sizeof(kmsg),"sfp%02d:%1d:%1d:%1d ",i+1,(tmp[0] == 0xff) ? 0:tmp[0],(tmp[1] == 0xff) ? 0:tmp[1],(tmp[2] == 0xff) ? 0:tmp[2]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
memset(tmp,0xff,sizeof(tmp));
|
|
for(i = 0;i < 24;i++)
|
|
{
|
|
if(!(irq_present_status_high_current & (0x1 << i)) && (irq_present_status_high_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d is present\r\n",i+25));
|
|
tmp[0] = 1;
|
|
}
|
|
else if((irq_present_status_high_current & (0x1 << i)) && !(irq_present_status_high_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d is absent\r\n",i+25));
|
|
tmp[0] = 0;
|
|
|
|
}
|
|
|
|
if(!(irq_rx_lost_status_high_current & (0x1 << i)) && (irq_rx_lost_status_high_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d optical is meet\r\n",i+25));
|
|
tmp[1] = 1;
|
|
}
|
|
else if((irq_rx_lost_status_high_current & (0x1 << i)) && !(irq_rx_lost_status_high_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d optical is lost\r\n",i+25));
|
|
tmp[1] = 0;
|
|
}
|
|
|
|
if(!(irq_tx_fault_status_high_current & (0x1 << i)) && (irq_tx_fault_status_high_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d transmission is right\r\n",i+25));
|
|
tmp[2] = 1;
|
|
}
|
|
else if((irq_tx_fault_status_high_current & (0x1 << i)) && !(irq_tx_fault_status_high_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d transmission is fault\r\n",i+25));
|
|
tmp[2] = 0;
|
|
}
|
|
|
|
if((tmp[0] != 0xff) || (tmp[1] != 0xff) || (tmp[2] != 0xff))
|
|
{
|
|
memset(kmsg,0xff,sizeof(kmsg));
|
|
snprintf(kmsg,sizeof(kmsg),"sfp%02d:%1d:%1d:%1d ",i+25,(tmp[0] == 0xff) ? 0:tmp[0],(tmp[1] == 0xff) ? 0:tmp[1],(tmp[2] == 0xff) ? 0:tmp[2]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
memset(tmp,0xff,sizeof(tmp));
|
|
for(i = 0 ; i < 8; i++)
|
|
{
|
|
if(!(irq_present_qsfp_current & (0x1 << i)) && (irq_present_qsfp_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d is present\r\n",i+49));
|
|
tmp[0] = 1;
|
|
}
|
|
else if((irq_present_qsfp_current & (0x1 << i)) && !(irq_present_qsfp_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d is absent\r\n",i+49));
|
|
tmp[0] = 0;
|
|
}
|
|
|
|
if(!(irq_interrupt_qsfp_current & (0x1 << i)) && (irq_interrupt_qsfp_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d interrupt is occured \r\n",i+49));
|
|
tmp[1] = 1;
|
|
}
|
|
else if((irq_interrupt_qsfp_current & (0x1 << i)) && !(irq_interrupt_qsfp_next & (0x1 << i)))
|
|
{
|
|
DEB2(printk("SFP%d interrupt is cleaned\r\n",i+49));
|
|
tmp[1] = 0;
|
|
}
|
|
|
|
if((tmp[0] != 0xff) || (tmp[1] != 0xff))
|
|
{
|
|
memset(kmsg,0xff,sizeof(kmsg));
|
|
snprintf(kmsg,sizeof(kmsg),"qsfp%02d:%1d:%1d:%1d ",i+49,(tmp[0] == 0xff) ? 0:tmp[0],(tmp[1] == 0xff) ? 0:tmp[1],0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
irq_present_status_low_next = irq_present_status_low_current;
|
|
irq_rx_lost_status_low_next = irq_rx_lost_status_low_current;
|
|
irq_tx_fault_status_low_next = irq_tx_fault_status_low_current;
|
|
irq_present_status_high_next = irq_present_status_high_current;
|
|
irq_rx_lost_status_high_next = irq_rx_lost_status_high_current;
|
|
irq_tx_fault_status_high_next = irq_tx_fault_status_high_current;
|
|
irq_present_qsfp_next = irq_present_qsfp_current;
|
|
irq_interrupt_qsfp_next = irq_interrupt_qsfp_current;
|
|
|
|
send_usrmsg(kmsg, strlen(kmsg));
|
|
}
|
|
|
|
static void disableIrq(unsigned short maskReg, unsigned short mask)
|
|
{
|
|
u8 data = 0;
|
|
|
|
data = lpc_iic_getbyte(NULL,maskReg);
|
|
data |= mask;
|
|
lpc_iic_setbyte(NULL,maskReg, data);
|
|
}
|
|
|
|
static void enableIrq(unsigned short maskReg, unsigned short mask)
|
|
{
|
|
unsigned short data;
|
|
|
|
data = lpc_iic_getbyte(NULL,maskReg);
|
|
data &= ~mask;
|
|
lpc_iic_setbyte(NULL,maskReg, data);
|
|
}
|
|
|
|
|
|
static irqreturn_t irq_inter_isr(int irq, void *handle)
|
|
{
|
|
|
|
/*
|
|
* use keventd context to read the event fifo registers
|
|
* Schedule readout at least 25ms after notification for
|
|
* REVID < 4
|
|
*/
|
|
|
|
schedule_delayed_work(&irq_inter_work, irq_inter_delay);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
|
|
#define CIG_CPLD_CHR_NAME "cpld"
|
|
|
|
|
|
static int __init cpld_init(void)
|
|
{
|
|
int rval,rc=0;
|
|
dev_t dev;
|
|
u8 s_data;
|
|
int isr_GPIO_num = 289;
|
|
|
|
DEB2(printk("cpld_init\n");)
|
|
|
|
/**************************************************************************************/
|
|
|
|
LPC_INDEX_REG = lpc_base_addr;
|
|
LPC_DATA_REG = lpc_base_addr + 1;
|
|
|
|
cpld_device = kzalloc(sizeof(struct cpld_dev_type), GFP_KERNEL);
|
|
if (!cpld_device)
|
|
goto error3;
|
|
cpld_device->io_resource = request_region(lpc_base_addr,
|
|
lpc_io_space_size, "lpc-i2c");
|
|
if (!cpld_device->io_resource) {
|
|
printk("lpc: claim I/O resource fail\n");
|
|
goto error2;
|
|
}
|
|
sema_init(&cpld_device->sem, 1);
|
|
|
|
if (cpld_major) {
|
|
dev = MKDEV(cpld_major, cpld_minor);
|
|
rc = register_chrdev_region(dev, 1, CIG_CPLD_CHR_NAME);
|
|
} else {
|
|
rc = alloc_chrdev_region(&dev, cpld_major, 1, CIG_CPLD_CHR_NAME);
|
|
cpld_major = MAJOR(dev);
|
|
}
|
|
|
|
cpld_setup_cdev(cpld_device);
|
|
|
|
cpld_class = class_create(THIS_MODULE,CIG_CPLD_CHR_NAME);
|
|
if (!cpld_class) {
|
|
DEB2(printk("failed to create class\n");)
|
|
goto error1;
|
|
}
|
|
|
|
cpld_class->p->subsys.kobj.ktype= &cpld_kobj_type;
|
|
cpld_sysfs_add_attr(&cpld_class->p->subsys.kobj, "read");
|
|
cpld_sysfs_add_attr(&cpld_class->p->subsys.kobj, "write");
|
|
cpld_sysfs_add_attr(&cpld_class->p->subsys.kobj, "reads");
|
|
cpld_sysfs_add_attr(&cpld_class->p->subsys.kobj, "writes");
|
|
cpld_sysfs_add_attr(&cpld_class->p->subsys.kobj, "version");
|
|
|
|
cpld_dev = device_create(cpld_class, NULL, dev, NULL, CIG_CPLD_CHR_NAME);
|
|
|
|
/**************************************************************************************/
|
|
|
|
rval = lpc_bus_init();
|
|
rval = lpc_register_driver(&i2c_lpc_driver, 1);
|
|
|
|
/**************************************************************************************/
|
|
return 0;
|
|
error1:
|
|
cdev_del(&cpld_device->cdev);
|
|
unregister_chrdev_region(dev, 1);
|
|
error2:
|
|
release_resource(cpld_device->io_resource);
|
|
error3:
|
|
kfree(cpld_device);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void __exit cpld_exit(void)
|
|
{
|
|
DEB2(printk("cpld_exit\n"));
|
|
|
|
if (nlsk){
|
|
netlink_kernel_release(nlsk); /* release ..*/
|
|
nlsk = NULL;
|
|
}
|
|
|
|
|
|
lpc_unregister_driver(&i2c_lpc_driver);
|
|
lpc_bus_exit();
|
|
dev_t devno = MKDEV(cpld_major, cpld_minor);
|
|
|
|
cdev_del(&cpld_device->cdev);
|
|
if (cpld_class) {
|
|
device_destroy(cpld_class, devno);
|
|
class_destroy(cpld_class);
|
|
}
|
|
|
|
if (cpld_device) {
|
|
if (cpld_device->io_resource)
|
|
release_resource(cpld_device->io_resource);
|
|
|
|
kfree(cpld_device);
|
|
}
|
|
unregister_chrdev_region(devno, 1);
|
|
|
|
|
|
}
|
|
|
|
module_param(cpld_major, int, S_IRUGO);
|
|
module_param(cpld_minor, int, S_IRUGO);
|
|
module_param(i2c_debug, int, S_IRUGO);
|
|
module_param(board_id, int, S_IRUGO);
|
|
|
|
module_init(cpld_init);
|
|
module_exit(cpld_exit);
|
|
|
|
MODULE_AUTHOR("Zhang Peng <zhangpeng@cigtech.com>");
|
|
MODULE_DESCRIPTION("cs5435-54p-cpld driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
/********************************************** End ********************************************************/
|
|
|
|
|
|
|