blob: 48e9ec47ae47a1510242622085fc122bb9f39088 [file] [log] [blame]
/*
<:copyright-BRCM:2016:DUAL/GPL:standard
Broadcom Proprietary and Confidential.(c) 2016 Broadcom
All Rights Reserved
Unless you and Broadcom execute a separate written software license
agreement governing use of this software, this software is licensed
to you under the terms of the GNU General Public License version 2
(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
with the following added to such license:
As a special exception, the copyright holders of this software give
you permission to link this software with independent modules, and
to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent
module, the terms and conditions of the license of that module.
An independent module is a module which is not derived from this
software. The special exception does not apply to any modifications
of the software.
Not withstanding the above, under no circumstances may you combine
this software in any way with any other Broadcom software provided
under a license other than the GPL, without Broadcom's express prior
written consent.
:>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/cdev.h>
#include <asm/uaccess.h> /*copy_from_user*/
#include <linux/proc_fs.h>
#include "bcmos_system.h"
#include "bcmolt_i2c_devs.h"
#include "bcmolt_i2c_devs_ioctl.h"
#define BCM_I2C_DEV_ADDR_START typedef enum {
#define BCM_I2C_DEV_ADDR(name, desc, val) name = val,
#define BCM_I2C_DEV_ADDR_END } bcm_i2c_dev_addr;
#include "bcmolt_i2c_devs_addr.h"
#define PROC_DIR_NAME "maple_i2c"
#define PROC_ENTRY_NAME "i2c"
#define MAX_EAGAIN_ITERS 200 /* 10ms per iter */
/* uncomment the next line to enable debug output to the kernel CLI */
/* #define I2C_DEBUG_PRINTS 1 */
#ifdef I2C_DEBUG_PRINTS
#define i2c_debug_print(fmt, args...) printk("%s#%d: " fmt, __FUNCTION__, __LINE__, ## args)
#else
#define i2c_debug_print(...)
#endif
int maple_i2c_chrdev_major = 216;
module_param(maple_i2c_chrdev_major, int, S_IRUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(maple_i2c_chrdev_major, "maple_i2c_major");
static struct proc_dir_entry *i2c_proc_dir;
static struct proc_dir_entry *i2c_proc_entry;
static char is_chrdev_reg;
static char is_cdev_add;
static int i2c_client_num;
static int i2c_num_clients;
const unsigned short normal_i2c[] = {
I2C_SW0_I2C_ADDR,
I2C_SW1_I2C_ADDR,
I2C_SW2_I2C_ADDR,
I2C_SW3_I2C_ADDR,
I2C_SW4_I2C_ADDR,
SFP_I2C_ADDR1,
SFP_I2C_ADDR2,
FPGA_I2C_ADDR,
PON_DPLL_I2C_ADDR,
PM_DPLL_I2C_ADDR,
CXP_R_I2C_ADDR,
PCIE_SW_I2C_ADDR,
I2C_CLIENT_END
};
static unsigned char slave_valid[SLAVE_NUM_OF_SLAVES] = {0};
/*
* Driver data (common to all clients)
*/
static const struct i2c_device_id maple_i2c_id_table[] = {
{ "maple_i2c", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, maple_i2c_id_table);
static void bcm_access_fpga(void);
/*
* Client data (each client gets its own)
*/
struct maple_i2c_data {
struct i2c_client client;
};
static struct maple_i2c_data *pclient_data_ar[SLAVE_NUM_OF_SLAVES];
static int get_client(unsigned char client_num, struct i2c_client **client)
{
if ((client_num >= SLAVE_NUM_OF_SLAVES) || !slave_valid[client_num])
{
i2c_debug_print("I2C error: Wrong client number 0x%x\n", client_num);
return -1;
}
*client = &pclient_data_ar[client_num]->client;
return 0;
}
/*
* Helper functions which attempt I2C transfer until successful, or error code is other than -EAGAIN
*/
static int maple_i2c_send(unsigned char client_num, u8 *buf, size_t count)
{
struct i2c_client *client;
int rc;
int niter = 0;
if (get_client(client_num, &client))
return -1;
do {
rc = i2c_master_send(client, buf, count);
if (rc == -EAGAIN)
msleep(10);
} while (rc == -EAGAIN && ++niter < MAX_EAGAIN_ITERS);
rc = (rc == count) ? 0 : (rc < 0 ) ? rc : -1;
return rc;
}
static int maple_i2c_recv(unsigned char client_num, u8 *buf, size_t count)
{
struct i2c_client *client;
int rc;
int niter = 0;
if (get_client(client_num, &client))
return -1;
do {
rc = i2c_master_recv(client, buf, count);
if (rc == -EAGAIN)
msleep(10);
} while (rc == -EAGAIN && ++niter < MAX_EAGAIN_ITERS);
rc = (rc == count) ? 0 : (rc < 0 ) ? rc : -1;
return rc;
}
static int maple_i2c_read_with_offset(unsigned char client_num, u8 *offset, size_t offset_size, u8 *buf, size_t count)
{
struct i2c_client *client;
struct i2c_msg msg[2];
int rc;
int niter = 0;
if (get_client(client_num, &client))
return -1;
msg[0].addr = msg[1].addr = client->addr;
msg[0].flags = msg[1].flags = client->flags & I2C_M_TEN;
msg[0].len = offset_size;
msg[0].buf = offset;
msg[1].flags |= I2C_M_RD;
msg[1].len = count;
msg[1].buf = buf;
do {
rc = i2c_transfer(client->adapter, msg, 2);
if (rc == -EAGAIN)
msleep(10);
} while (rc == -EAGAIN && ++niter < MAX_EAGAIN_ITERS);
rc = (rc == 2) ? 0 : (rc < 0 ) ? rc : -1;
return rc;
}
ssize_t maple_i2c_write(unsigned char client_num, char *buf, size_t count)
{
int rc;
i2c_debug_print("Write to switch: client=0x%x val = 0x%x\n", normal_i2c[client_num], *buf);
rc = maple_i2c_send(client_num, (u8 *)buf, count);
if (rc < 0)
i2c_debug_print("Write Failed. rc=%d client=%u[%u]\n", rc,
normal_i2c[client_num], client_num);
else
i2c_debug_print("Write Successful client=%u[%u]\n",
normal_i2c[client_num], client_num);
return rc;
}
EXPORT_SYMBOL(maple_i2c_write);
ssize_t maple_i2c_read(unsigned char client_num, char *buf, size_t count)
{
int rc;
i2c_debug_print("Read from switch: client 0x%x\n", normal_i2c[client_num]);
rc = maple_i2c_recv(client_num, (u8 *)buf, count);
if (rc < 0)
i2c_debug_print("Read Failed. rc=%d client=%u[%u]\n", rc,
normal_i2c[client_num], client_num);
else
i2c_debug_print("Read Successful: count=%u val = 0x%x\n", (unsigned)count, *buf);
return rc;
}
EXPORT_SYMBOL(maple_i2c_read);
int maple_i2c_write_byte(unsigned char client_num, unsigned char offset, unsigned char val)
{
u8 buf[2];
int rc;
i2c_debug_print("Write Byte: client=0x%x offset = 0x%x, val = 0x%x\n", normal_i2c[client_num], offset, val);
buf[0] = offset;
buf[1] = val;
rc = maple_i2c_send(client_num, buf, 2);
if (rc < 0)
i2c_debug_print("Write Byte Failed. rc=%d offset=0x%x val=0x%x client=%u[%u]\n",
rc, offset, val,
normal_i2c[client_num], client_num);
else
i2c_debug_print("Write Byte: offset=0x%x val=0x%x client=%u/%u\n",
offset, val,
normal_i2c[client_num], client_num);
return rc;
}
EXPORT_SYMBOL(maple_i2c_write_byte);
int maple_i2c_read_byte(unsigned char client_num, unsigned char offset, unsigned char *data)
{
u8 o = offset;
u8 val;
int rc;
i2c_debug_print("Write Byte: client=0x%x offset = 0x%x, val = 0x%x\n", normal_i2c[client_num], offset, val);
rc = maple_i2c_read_with_offset(client_num, &o, 1, &val, 1);
if (rc < 0)
i2c_debug_print("Read Byte Failed. rc=%d client=%u[%u]\n", rc,
normal_i2c[client_num], client_num);
else
{
*data = val;
i2c_debug_print("Read Byte: client=0x%x offset = 0x%x, val = 0x%x\n",
normal_i2c[client_num], offset, *data);
}
return rc;
}
EXPORT_SYMBOL(maple_i2c_read_byte);
int maple_i2c_write_word(unsigned char client_num, unsigned char offset, unsigned short val)
{
u8 buf[3];
int rc;
i2c_debug_print("Write Word: offset = 0x%x, val = 0x%x\n", offset, val);
/* The offset to be written should be the first byte in the I2C write */
buf[0] = offset;
buf[1] = (char)(val&0xFF);
buf[2] = (char)(val>>8);
rc = maple_i2c_send(client_num, buf, sizeof(buf));
if (rc < 0)
i2c_debug_print("Write Word Failed. rc=%d offset=0x%x val=0x%x, client=%u[%u]\n",
rc, offset, val,
normal_i2c[client_num], client_num);
else
i2c_debug_print("Write Word: offset=0x%x val=0x%x, client=%u[%u]\n",
offset, val,
normal_i2c[client_num], client_num);
return rc;
}
EXPORT_SYMBOL(maple_i2c_write_word);
int maple_i2c_read_word(unsigned char client_num, unsigned char offset, unsigned short *data)
{
u8 o = offset;
u8 buf[2];
int rc;
rc = maple_i2c_read_with_offset(client_num, &o, 1, buf, sizeof(buf));
if (rc < 0)
i2c_debug_print("Read Word Failed. rc=%d offset=0x%x client=%u[%u]\n",
rc, offset,
normal_i2c[client_num], client_num);
else
{
*data = (buf[1]<<8) | buf[0]; /* I2C data is LE */
i2c_debug_print("Read Word: offset = 0x%x, val = 0x%x client=%u[%u]\n",
offset, *data,
normal_i2c[client_num], client_num);
}
return rc;
}
EXPORT_SYMBOL(maple_i2c_read_word);
int maple_i2c_write_reg(unsigned char client_num, unsigned char offset, int val)
{
u8 buf[5];
int rc;
i2c_debug_print("Write Register: offset = 0x%x, val = 0x%x\n", offset, val);
/* Set the buf[0] to be the offset for write operation */
buf[0] = offset;
/* On the I2C bus, LS Byte should go first */
buf[1] = val & 0xff;
buf[2] = (val >> 8) & 0xff;
buf[3] = (val >> 16) & 0xff;
buf[4] = (val >> 24) & 0xff;
rc = maple_i2c_send(client_num, buf, sizeof(buf));
if (rc < 0)
i2c_debug_print("Write Reg Failed. rc=%d offset=0x%x val=0x%x client=%u[%u]\n",
rc, offset, val,
normal_i2c[client_num], client_num);
else
i2c_debug_print("Write Reg Successful. offset=0x%x val=0x%x client=%u[%u]\n",
offset, val,
normal_i2c[client_num], client_num);
return rc;
}
EXPORT_SYMBOL(maple_i2c_write_reg);
int maple_i2c_read_reg(unsigned char client_num, unsigned char offset, unsigned int *data)
{
u8 o = offset;
u8 buf[4];
int rc;
rc = maple_i2c_read_with_offset(client_num, &o, 1, buf, sizeof(buf));
if (rc < 0)
i2c_debug_print("Read Reg Failed. rc=%d offset=0x%x client=%u[%u]\n",
rc, offset,
normal_i2c[client_num], client_num);
else
{
*data = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); /* LS byte is transferred first */
i2c_debug_print("Read Register: client=0x%x offset = 0x%x, val = 0x%x\n", normal_i2c[client_num], offset, *data);
}
return rc;
}
EXPORT_SYMBOL(maple_i2c_read_reg);
int maple_i2c_write_fpga(unsigned char client_num, unsigned int offset, unsigned int val)
{
u8 buf[8];
int rc;
bcm_access_fpga();
i2c_debug_print("Write fpga: offset = 0x%x, val = 0x%x\n", offset, val);
/* Set the offset for write operation */
// offset = swab32(offset);
memcpy(&buf[0], (char*)&offset, 4);
/* On the I2C bus, LS Byte should go first */
// val = swab32(val);
memcpy(&buf[4], (char*)&val, 4);
rc = maple_i2c_send(client_num, buf, sizeof(buf));
if (rc < 0)
i2c_debug_print("Write FPGA Failed. rc=%d offset=0x%x val=0x%x client=%u[%u]\n",
rc, offset, val,
normal_i2c[client_num], client_num);
else
i2c_debug_print("Write FPGA: offset=0x%x val=0x%x client=%u[%u]\n",
offset, val,
normal_i2c[client_num], client_num);
return rc;
}
EXPORT_SYMBOL(maple_i2c_write_fpga);
int maple_i2c_read_fpga(unsigned char client_num, unsigned int offset, unsigned int *data)
{
u8 obuf[4];
u8 buf[4];
int rc;
bcm_access_fpga();
/* FPGA expects 4 byte offset in BIG ENDIAN format */
obuf[0] = (offset >> 24) & 0xff;
obuf[1] = (offset >> 16) & 0xff;
obuf[2] = (offset >> 8) & 0xff;
obuf[3] = offset & 0xff;
rc = maple_i2c_read_with_offset(client_num, obuf, sizeof(obuf), buf, sizeof(buf));
if (rc < 0)
i2c_debug_print("Read FPGA Failed. rc=%d offset=0x%x client=%u[%u]\n",
rc, offset,
normal_i2c[client_num], client_num);
else
{
*data = buf[3] | (buf[2] << 8) | (buf[1] << 16) | (buf[0] << 24); /* MS byte is transferred first */
i2c_debug_print("Read FPGA: offset = 0x%x, val = 0x%x %02x %02x %02x %02x client=%u[%u]\n",
offset, *data, buf[0], buf[1], buf[2], buf[3],
normal_i2c[client_num], client_num);
}
return rc;
}
EXPORT_SYMBOL(maple_i2c_read_fpga);
enum client_num maple_i2c_get_client(u32 addr)
{
enum client_num i = 0;
while (i < SLAVE_NUM_OF_SLAVES)
{
if (pclient_data_ar[i])
{
if (slave_valid[i] && pclient_data_ar[i]->client.addr == addr)
{
break;
}
}
i++;
}
return i;
}
EXPORT_SYMBOL(maple_i2c_get_client);
static int dev_change(u32 addr)
{
enum client_num i = maple_i2c_get_client(addr);
if (i < SLAVE_NUM_OF_SLAVES)
{
i2c_client_num = i;
i2c_debug_print("Slave was set successfully i %d, addr 0x%x\n", i, addr);
}
else
{
i2c_debug_print("Failed to set slave i %d, addr 0x%x\n", i, addr);
}
return 0;
}
static void bcm_access_fpga(void)
{
static int access_enabled;
int fpga_client_num = maple_i2c_get_client(I2C_SW1_I2C_ADDR);
uint8_t b;
if (access_enabled)
return;
b = 4;
maple_i2c_write(fpga_client_num, &b, 1);
access_enabled = 1;
}
#define BCM_FPGA_REG_RESETS 0x2
#define BCM_FPGA_REG_FPGA_VERSION 0x0
/* 1 = out of reset bcm_board_config_reset_value */
void reset_maple(int bit,int param)
{
int fpga_client_num = maple_i2c_get_client(FPGA_I2C_ADDR);
int sw_client_num = maple_i2c_get_client(I2C_SW0_I2C_ADDR);
uint8_t b = 1;
uint32_t reset_values;
maple_i2c_write(sw_client_num, &b, 1);
/* Read-modify-write so we can set only the relevant bit */
maple_i2c_read_fpga(fpga_client_num, BCM_FPGA_REG_RESETS, &reset_values);
if (param)
reset_values |= 1 << bit;
else
reset_values &= ~(1 << bit);
maple_i2c_write_fpga(fpga_client_num, BCM_FPGA_REG_RESETS, reset_values);
}
EXPORT_SYMBOL(reset_maple);
/* Calls the appropriate function based on user command */
static int exec_command(const char *buf, size_t count)
{
#define MAX_ARGS 4
#define MAX_ARG_SIZE 32
int i, argc = 0, val = 0;
char cmd;
u32 offset = 0;
char arg[MAX_ARGS][MAX_ARG_SIZE];
#define LOG_WR_KBUF_SIZE 128
char kbuf[LOG_WR_KBUF_SIZE];
if ((count > LOG_WR_KBUF_SIZE-1) || (copy_from_user(kbuf, buf, count) != 0))
return -EFAULT;
kbuf[count]=0;
argc = sscanf(kbuf, "%c %s %s %s %s", &cmd, arg[0], arg[1], arg[2], arg[3]);
if (argc < 1)
{
printk("Need at-least 2 arguments\n");
return -EFAULT;
}
for (i=0; i<MAX_ARGS; ++i) {
arg[i][MAX_ARG_SIZE-1] = '\0';
}
offset = (u32)simple_strtoul(arg[0], NULL, 0);
if (argc == 3)
val = (int)simple_strtoul(arg[1], NULL, 0);
switch (cmd)
{
case 'm':
reset_maple(0,offset); // maple out of reset(1) | reset (0)
break;
case 'k':
reset_maple(4,offset); // katana2 out of reset(1) | reset (0)
break;
case 'b':
if (argc == 3)
maple_i2c_write_byte(i2c_client_num, (u8)offset, (u8)val);
else
{
u8 data;
maple_i2c_read_byte(i2c_client_num, (u8)offset, &data);
printk("client 0x%x, offset 0x%x, data=0x%x\n", normal_i2c[i2c_client_num], offset, data);
}
break;
case 's':
if (argc == 2)
{
u8 data = (u8)offset;
maple_i2c_write(i2c_client_num, &data, 1);
}
else
{
u8 data = offset;
maple_i2c_read(i2c_client_num, &data, 1);
printk("client 0x%x, data=0x%x\n", normal_i2c[i2c_client_num], data);
}
break;
case 'w':
if (argc == 3)
maple_i2c_write_word(i2c_client_num, (u8)offset, (u16)val);
else
{
u16 data;
maple_i2c_read_word(i2c_client_num, (u8)offset, &data);
printk("client 0x%x, offset 0x%x, data=0x%x\n", normal_i2c[i2c_client_num], offset, data);
}
break;
case 'd':
if (argc == 3)
maple_i2c_write_reg(i2c_client_num, (u8)offset, val);
else
{
u32 data;
maple_i2c_read_reg(i2c_client_num, (u8)offset, &data);
printk("client 0x%x, offset 0x%x, data=0x%x\n", normal_i2c[i2c_client_num], offset, data);
}
break;
case 'f':
if (argc == 3)
maple_i2c_write_fpga(i2c_client_num, offset, val);
else
{
u32 data;
maple_i2c_read_fpga(i2c_client_num, offset, &data);
printk("client 0x%x, offset 0x%x, data=0x%x\n", normal_i2c[i2c_client_num], offset, data);
}
break;
case 'c':
dev_change(offset);
break;
default:
printk("Invalid command.\n Valid commands:\n"
" Change I2C Addr: c addr\n"
" Write Reg: d offset val\n"
" Read Reg: d offset\n"
" Write Word: w offset val\n"
" Read Word: w offset\n"
" Write Byte: b offset val\n"
" Read Byte: b offset\n"
" Write fpga: f offset val\n"
" Read fpga: f offset\n"
" Write to switch: s val (one byte)\n"
" Read from switch:s\n"
" Maple: m <0|1>\n"
" Katana2: k <0|1>\n"
);
break;
}
return count;
}
/* Read Function of PROCFS attribute "maple_i2c/test" */
static ssize_t maple_i2c_proc_test_read(struct file *f, char *buf, size_t count,
loff_t *pos)
{
printk(" Usage: echo command > "
" /proc/maple_i2c/test\n");
printk(" supported commands:\n"
" Change I2C Addr: c addr\n"
" Write Reg: d offset val\n"
" Read Reg: d offset\n"
" Write Word: w offset val\n"
" Read Word: w offset\n"
" Write Byte: b offset val\n"
" Read Byte: b offset\n"
" Write fpga: f offset val\n"
" Read fpga: f offset\n"
" Write to switch: s val (one byte)\n"
" Read from switch:s\n"
);
return 0;
}
/* Write Function of PROCFS attribute "maple_i2c/test" */
static ssize_t maple_i2c_proc_test_write(struct file *f, const char *buf,
size_t count, loff_t *pos)
{
return exec_command(buf, count);
}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int maple_i2c_dev_detect(struct i2c_client *client, struct i2c_board_info *info)
{
i2c_debug_print("detecting i2c device 0x%x\n", client->addr);
/* TODO: detection and identification */
strcpy(info->type, "maple_i2c");
info->flags = 0;
return 0;
}
static struct file_operations maple_i2c_fops =
{
read: maple_i2c_proc_test_read,
write: maple_i2c_proc_test_write
};
static int maple_i2c_dev_create_proc(void)
{
i2c_proc_dir = proc_mkdir(PROC_DIR_NAME, NULL);
if (!i2c_proc_dir)
{
i2c_debug_print("fail to create proc dir\n");
return -ENOMEM;
}
i2c_proc_entry = create_proc_entry(PROC_ENTRY_NAME, 0, i2c_proc_dir);
if (!i2c_proc_entry)
{
remove_proc_entry(PROC_DIR_NAME, NULL);
return -ENOMEM;
}
i2c_proc_entry->proc_fops = &maple_i2c_fops;
return 0;
}
static int maple_i2c_chrdev_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int maple_i2c_chrdev_release(struct inode *inode, struct file *filp)
{
return 0;
}
static long maple_i2c_chrdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int linux_rc = 0;
int rc;
maple_i2c_ioctl_param params;
/* don't even decode wrong cmds: better returning ENOTTY than EFAULT */
if (_IOC_TYPE(cmd) != MAPLE_I2C_IOCTL_MAGIC)
return -ENOTTY;
/*
* the type is a bitmask, and VERIFY_WRITE catches R/W
* transfers. Note that the type is user-oriented, while
* verify_area is kernel-oriented, so the concept of "read" and
* "write" is reversed
*/
if (_IOC_DIR(cmd) & _IOC_READ)
linux_rc = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
linux_rc = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (linux_rc)
return -EFAULT;
switch (cmd)
{
case MAPLE_I2C_IOCTL_OP_DEV_CHANGE:
{
linux_rc = copy_from_user((char *)&params, (char *)arg, sizeof(params));
if (linux_rc < 0)
break;
return dev_change(params.addr);
}
break;
case MAPLE_I2C_IOCTL_OP_DEV_WRITE:
{
linux_rc = copy_from_user((char *)&params, (char *)arg, sizeof(params));
if (linux_rc < 0)
break;
switch (params.count)
{
case 8:
return maple_i2c_write_byte(i2c_client_num, params.addr, params.val);
case 16:
return maple_i2c_write_word(i2c_client_num, params.addr, params.val);
case 32:
return maple_i2c_write_reg(i2c_client_num, params.addr, params.val);
default:
return -EINVAL;
}
}
break;
case MAPLE_I2C_IOCTL_OP_DEV_READ:
{
unsigned char b;
unsigned short w;
unsigned int r;
linux_rc = copy_from_user((char *)&params, (char *)arg, sizeof(params));
if (linux_rc < 0)
break;
switch (params.count)
{
case 8:
linux_rc = maple_i2c_read_byte(i2c_client_num, params.addr, &b);
if (linux_rc < 0)
break;
params.val = b;
break;
case 16:
linux_rc = maple_i2c_read_word(i2c_client_num, params.addr, &w);
if (linux_rc < 0)
break;
params.val = w;
break;
case 32:
linux_rc = maple_i2c_read_reg(i2c_client_num, params.addr, &r);
if (linux_rc < 0)
break;
params.val = r;
break;
default:
return -EINVAL;
}
if (linux_rc < 0)
break;
return copy_to_user((char *)arg, (char *)&params, sizeof(params));
}
case MAPLE_I2C_IOCTL_OP_SWITCH_WRITE:
{
char b;
linux_rc = copy_from_user((char *)&params, (char *)arg, sizeof(params));
if (linux_rc < 0)
break;
b = params.val;
return maple_i2c_write(i2c_client_num, &b, 1);
}
case MAPLE_I2C_IOCTL_OP_SWITCH_READ:
{
char b;
linux_rc = maple_i2c_read(i2c_client_num, &b, 1);
if (linux_rc < 0)
break;
params.val = b;
return copy_to_user((char *)arg, (char *)&params, sizeof(params));
}
case MAPLE_I2C_IOCTL_OP_FPGA_WRITE:
{
linux_rc = copy_from_user((char *)&params, (char *)arg, sizeof(params));
if (linux_rc < 0)
break;
return maple_i2c_write_fpga(i2c_client_num, params.addr, params.val);
}
case MAPLE_I2C_IOCTL_OP_FPGA_READ:
{
linux_rc = copy_from_user((char *)&params, (char *)arg, sizeof(params));
if (linux_rc < 0)
break;
linux_rc = maple_i2c_read_fpga(i2c_client_num, params.addr, &params.val);
if (linux_rc < 0)
break;
return copy_to_user((char *)arg, (char *)&params, sizeof(params));
}
default:
rc = -ENOTTY;
break;
}
return linux_rc;
}
static ssize_t maple_i2c_chrdev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
return 0;
}
static struct file_operations maple_i2c_chrdev_fops =
{
.owner = THIS_MODULE,
.open = maple_i2c_chrdev_open,
.release = maple_i2c_chrdev_release,
.write = maple_i2c_chrdev_write,
.unlocked_ioctl = maple_i2c_chrdev_ioctl
};
static struct cdev maple_i2c_chrdev_cdev;
static int maple_i2c_dev_create_chrdev(void)
{
dev_t dev = MKDEV(maple_i2c_chrdev_major, 0);
int linux_rc;
is_chrdev_reg = 0;
is_cdev_add = 0;
/*
* Register your major, and accept a dynamic number.
*/
if (!maple_i2c_chrdev_major)
return -1;
linux_rc = register_chrdev_region(dev, 0, "maple_i2c");
if (linux_rc < 0)
{
i2c_debug_print("register_chrdev_region()->%d\n", linux_rc);
return -EIO;
}
is_chrdev_reg = 1;
cdev_init(&maple_i2c_chrdev_cdev, &maple_i2c_chrdev_fops);
linux_rc = cdev_add(&maple_i2c_chrdev_cdev, dev, 1);
if (linux_rc < 0)
{
i2c_debug_print("cdev_add()->%d\n", linux_rc);
return -EIO;
}
is_cdev_add = 1;
return 0;
}
static int maple_i2c_dev_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int err = 0;
struct maple_i2c_data *pclient_data;
i2c_debug_print("!!!! i2c device 0x%x probe\n", client->addr);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
goto exit;
if (!(pclient_data = kzalloc(sizeof(struct maple_i2c_data), GFP_KERNEL)))
{
err = -ENOMEM;
goto exit;
}
pclient_data->client.addr = client->addr;
pclient_data->client.adapter = client->adapter;
pclient_data->client.driver = client->driver;
pclient_data->client.flags = client->flags;
i2c_set_clientdata(client, pclient_data);
switch(client->addr)
{
case I2C_SW0_I2C_ADDR:
pclient_data_ar[SLAVE_SWITCH_70] = pclient_data;
slave_valid[SLAVE_SWITCH_70] = 1;
break;
case I2C_SW1_I2C_ADDR:
pclient_data_ar[SLAVE_SWITCH_71] = pclient_data;
slave_valid[SLAVE_SWITCH_71] = 1;
break;
case I2C_SW2_I2C_ADDR:
pclient_data_ar[SLAVE_SWITCH_72] = pclient_data;
slave_valid[SLAVE_SWITCH_72] = 1;
break;
case I2C_SW3_I2C_ADDR:
pclient_data_ar[SLAVE_SWITCH_73] = pclient_data;
slave_valid[SLAVE_SWITCH_73] = 1;
break;
case I2C_SW4_I2C_ADDR:
pclient_data_ar[SLAVE_SWITCH_74] = pclient_data;
slave_valid[SLAVE_SWITCH_74] = 1;
break;
case SFP_I2C_ADDR1:
pclient_data_ar[SLAVE_SFP_50] = pclient_data;
slave_valid[SLAVE_SFP_50] = 1;
break;
case SFP_I2C_ADDR2:
pclient_data_ar[SLAVE_SFP_51] = pclient_data;
slave_valid[SLAVE_SFP_51] = 1;
break;
case FPGA_I2C_ADDR:
pclient_data_ar[SLAVE_FPGA_40] = pclient_data;
slave_valid[SLAVE_FPGA_40] = 1;
break;
case PON_DPLL_I2C_ADDR:
pclient_data_ar[SLAVE_PON_DPLL_68] = pclient_data;
slave_valid[SLAVE_PON_DPLL_68] = 1;
break;
case PM_DPLL_I2C_ADDR:
pclient_data_ar[SLAVE_PM_DPLL_6A] = pclient_data;
slave_valid[SLAVE_PM_DPLL_6A] = 1;
break;
case CXP_R_I2C_ADDR:
pclient_data_ar[SLAVE_CXP_R_54] = pclient_data;
slave_valid[SLAVE_CXP_R_54] = 1;
break;
case PCIE_SW_I2C_ADDR:
pclient_data_ar[SLAVE_PCIE_SW_3C] = pclient_data;
slave_valid[SLAVE_PCIE_SW_3C] = 1;
break;
default:
i2c_debug_print("%s client addr out of range 0x%x\n", __FUNCTION__, client->addr);
goto exit_kfree;
}
/* Create only once */
if (i2c_proc_entry == NULL)
{
if (maple_i2c_dev_create_proc() < 0)
goto exit_kfree;
}
if (!is_cdev_add)
{
if (maple_i2c_dev_create_chrdev() < 0)
goto exit_kfree;
}
++i2c_num_clients;
return 0;
exit_kfree:
kfree(pclient_data);
exit:
return err;
}
static int maple_i2c_dev_remove(struct i2c_client *client)
{
kfree(i2c_get_clientdata(client));
--i2c_num_clients;
if (!i2c_num_clients)
{
if (i2c_proc_entry != NULL)
{
remove_proc_entry(PROC_ENTRY_NAME, i2c_proc_dir);
i2c_proc_entry = NULL;
}
if (i2c_proc_dir != NULL)
{
remove_proc_entry(PROC_DIR_NAME, NULL);
i2c_proc_dir = NULL;
}
if (is_cdev_add)
{
cdev_del(&maple_i2c_chrdev_cdev);
is_cdev_add = 0;
}
if (is_chrdev_reg)
{
unregister_chrdev_region(MKDEV(maple_i2c_chrdev_major, 0), 1);
is_chrdev_reg = 0;
}
}
return 0;
}
static struct i2c_driver maple_i2c_dev_driver = {
.class = ~0,
.driver = {
.name = "maple_i2c",
},
.probe = maple_i2c_dev_probe,
.remove = maple_i2c_dev_remove,
.id_table = maple_i2c_id_table,
.detect = maple_i2c_dev_detect,
.address_list = normal_i2c,
};
module_i2c_driver(maple_i2c_dev_driver);
MODULE_DESCRIPTION("maple i2c devices");
MODULE_LICENSE("GPL");