我们从2011年坚守至今,只想做存粹的技术论坛。  由于网站在外面,点击附件后要很长世间才弹出下载,请耐心等待,勿重复点击不要用Edge和IE浏览器下载,否则提示不安全下载不了

 找回密码
 立即注册
搜索
查看: 2039|回复: 1

RK3288 I2C控制器的驱动源码

[复制链接]

该用户从未签到

812

主题

399

回帖

499

积分

二级逆天

积分
499

社区居民社区明星忠实会员宣传大使奖终身成就奖特殊贡献奖

QQ
发表于 2015-7-7 09:30:21 | 显示全部楼层 |阅读模式
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/idr.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/completion.h>
#include <linux/hardirq.h>
#include <linux/irqflags.h>
#include <linux/rwsem.h>
#include <linux/pm_runtime.h>
#include <asm/uaccess.h>

#include "i2c-core.h"


/* core_lock protects i2c_adapter_idr, and guarantees
   that device detection, deletion of detected devices, and attach_adapter
   and detach_adapter calls are serialized */
static DEFINE_MUTEX(core_lock);
static DEFINE_IDR(i2c_adapter_idr);

static struct device_type i2c_client_type;
//static int i2c_check_addr(struct i2c_adapter *adapter, int addr);
static int i2c_check_addr_ex(struct i2c_adapter *adapter, int addr);
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);

/* ------------------------------------------------------------------------- */
#ifdef CONFIG_I2C_DEV_RK29
extern struct completion        i2c_dev_complete;
extern void i2c_dev_dump_start(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
extern void i2c_dev_dump_stop(struct i2c_adapter *adap, struct i2c_msg *msgs, int num, int ret);
#endif
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
                        const struct i2c_client *client)
{
    while (id->name[0]) {
        if (strcmp(client->name, id->name) == 0)
            return id;
        id++;
    }
    return NULL;
}

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    struct i2c_client    *client = i2c_verify_client(dev);
    struct i2c_driver    *driver;

    if (!client)
        return 0;

    /* Attempt an OF style match */
    if (of_driver_match_device(dev, drv))
        return 1;

    driver = to_i2c_driver(drv);
    /* match on an id table if there is one */
    if (driver->id_table)
        return i2c_match_id(driver->id_table, client) != NULL;

    return 0;
}

#ifdef    CONFIG_HOTPLUG

/* uevent helps with hotplug: modprobe -q $(MODALIAS) */
static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
    struct i2c_client    *client = to_i2c_client(dev);

    if (add_uevent_var(env, "MODALIAS=%s%s",
               I2C_MODULE_PREFIX, client->name))
        return -ENOMEM;
    dev_dbg(dev, "uevent\n");
    return 0;
}

#else
#define i2c_device_uevent    NULL
#endif    /* CONFIG_HOTPLUG */

static int i2c_device_probe(struct device *dev)
{
    struct i2c_client    *client = i2c_verify_client(dev);
    struct i2c_driver    *driver;
    int status;

    if (!client)
        return 0;

    driver = to_i2c_driver(dev->driver);
    if (!driver->probe || !driver->id_table)
        return -ENODEV;
    client->driver = driver;
    if (!device_can_wakeup(&client->dev))
        device_init_wakeup(&client->dev,
                    client->flags & I2C_CLIENT_WAKE);
    dev_dbg(dev, "probe\n");

    status = driver->probe(client, i2c_match_id(driver->id_table, client));
    if (status) {
        client->driver = NULL;
        i2c_set_clientdata(client, NULL);
    }
    return status;
}

static int i2c_device_remove(struct device *dev)
{
    struct i2c_client    *client = i2c_verify_client(dev);
    struct i2c_driver    *driver;
    int            status;

    if (!client || !dev->driver)
        return 0;

    driver = to_i2c_driver(dev->driver);
    if (driver->remove) {
        dev_dbg(dev, "remove\n");
        status = driver->remove(client);
    } else {
        dev->driver = NULL;
        status = 0;
    }
    if (status == 0) {
        client->driver = NULL;
        i2c_set_clientdata(client, NULL);
    }
    return status;
}

static void i2c_device_shutdown(struct device *dev)
{
    struct i2c_client *client = i2c_verify_client(dev);
    struct i2c_driver *driver;

    if (!client || !dev->driver)
        return;
    driver = to_i2c_driver(dev->driver);
    if (driver->shutdown)
        driver->shutdown(client);
}

#ifdef CONFIG_PM_SLEEP
static int i2c_legacy_suspend(struct device *dev, pm_message_t mesg)
{
    struct i2c_client *client = i2c_verify_client(dev);
    struct i2c_driver *driver;

    if (!client || !dev->driver)
        return 0;
    driver = to_i2c_driver(dev->driver);
    if (!driver->suspend)
        return 0;
    return driver->suspend(client, mesg);
}

static int i2c_legacy_resume(struct device *dev)
{
    struct i2c_client *client = i2c_verify_client(dev);
    struct i2c_driver *driver;

    if (!client || !dev->driver)
        return 0;
    driver = to_i2c_driver(dev->driver);
    if (!driver->resume)
        return 0;
    return driver->resume(client);
}

static int i2c_device_pm_suspend(struct device *dev)
{
    const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

    if (pm)
        return pm_generic_suspend(dev);
    else
        return i2c_legacy_suspend(dev, PMSG_SUSPEND);
}

static int i2c_device_pm_resume(struct device *dev)
{
    const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

    if (pm)
        return pm_generic_resume(dev);
    else
        return i2c_legacy_resume(dev);
}

static int i2c_device_pm_freeze(struct device *dev)
{
    const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

    if (pm)
        return pm_generic_freeze(dev);
    else
        return i2c_legacy_suspend(dev, PMSG_FREEZE);
}

static int i2c_device_pm_thaw(struct device *dev)
{
    const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

    if (pm)
        return pm_generic_thaw(dev);
    else
        return i2c_legacy_resume(dev);
}

static int i2c_device_pm_poweroff(struct device *dev)
{
    const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

    if (pm)
        return pm_generic_poweroff(dev);
    else
        return i2c_legacy_suspend(dev, PMSG_HIBERNATE);
}

static int i2c_device_pm_restore(struct device *dev)
{
    const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

    if (pm)
        return pm_generic_restore(dev);
    else
        return i2c_legacy_resume(dev);
}
#else /* !CONFIG_PM_SLEEP */
#define i2c_device_pm_suspend    NULL
#define i2c_device_pm_resume    NULL
#define i2c_device_pm_freeze    NULL
#define i2c_device_pm_thaw    NULL
#define i2c_device_pm_poweroff    NULL
#define i2c_device_pm_restore    NULL
#endif /* !CONFIG_PM_SLEEP */

static void i2c_client_dev_release(struct device *dev)
{
    kfree(to_i2c_client(dev));
}

static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "%s\n", dev->type == &i2c_client_type ?
               to_i2c_client(dev)->name : to_i2c_adapter(dev)->name);
}

static ssize_t
show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name);
}

static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);

static struct attribute *i2c_dev_attrs[] = {
    &dev_attr_name.attr,
    /* modalias helps coldplug:  modprobe $(cat .../modalias) */
    &dev_attr_modalias.attr,
    NULL
};

static struct attribute_group i2c_dev_attr_group = {
    .attrs        = i2c_dev_attrs,
};

static const struct attribute_group *i2c_dev_attr_groups[] = {
    &i2c_dev_attr_group,
    NULL
};

static const struct dev_pm_ops i2c_device_pm_ops = {
    .suspend = i2c_device_pm_suspend,
    .resume = i2c_device_pm_resume,
    .freeze = i2c_device_pm_freeze,
    .thaw = i2c_device_pm_thaw,
    .poweroff = i2c_device_pm_poweroff,
    .restore = i2c_device_pm_restore,
    SET_RUNTIME_PM_OPS(
        pm_generic_runtime_suspend,
        pm_generic_runtime_resume,
        pm_generic_runtime_idle
    )
};

struct bus_type i2c_bus_type = {
    .name        = "i2c",
    .match        = i2c_device_match,
    .probe        = i2c_device_probe,
    .remove        = i2c_device_remove,
    .shutdown    = i2c_device_shutdown,
    .pm        = &i2c_device_pm_ops,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);

static struct device_type i2c_client_type = {
    .groups        = i2c_dev_attr_groups,
    .uevent        = i2c_device_uevent,
    .release    = i2c_client_dev_release,
};


/**
* i2c_verify_client - return parameter as i2c_client, or NULL
* @dev: device, probably from some driver model iterator
*
* When traversing the driver model tree, perhaps using driver model
* iterators like @device_for_each_child(), you can't assume very much
* about the nodes you find.  Use this function to avoid oopses caused
* by wrongly treating some non-I2C device as an i2c_client.
*/
struct i2c_client *i2c_verify_client(struct device *dev)
{
    return (dev->type == &i2c_client_type)
            ? to_i2c_client(dev)
            : NULL;
}
EXPORT_SYMBOL(i2c_verify_client);


/* This is a permissive address validity check, I2C address map constraints
* are purposely not enforced, except for the general call address. */
static int i2c_check_client_addr_validity(const struct i2c_client *client)
{
    if (client->flags & I2C_CLIENT_TEN) {
        /* 10-bit address, all values are valid */
        if (client->addr > 0x3ff)
            return -EINVAL;
    } else {
        /* 7-bit address, reject the general call address */
        if (client->addr == 0x00 || client->addr > 0x7f)
            return -EINVAL;
    }
    return 0;
}

/* And this is a strict address validity check, used when probing. If a
* device uses a reserved address, then it shouldn't be probed. 7-bit
* addressing is assumed, 10-bit address devices are rare and should be
* explicitly enumerated. */
static int i2c_check_addr_validity(unsigned short addr)
{
    /*
     * Reserved addresses per I2C specification:
     *  0x00       General call address / START byte
     *  0x01       CBUS address
     *  0x02       Reserved for different bus format
     *  0x03       Reserved for future purposes
     *  0x04-0x07  Hs-mode master code
     *  0x78-0x7b  10-bit slave addressing
     *  0x7c-0x7f  Reserved for future purposes
     */
    if (addr < 0x08 || addr > 0x77)
        return -EINVAL;
    return 0;
}

static int __i2c_check_addr_busy(struct device *dev, void *addrp)
{
    struct i2c_client    *client = i2c_verify_client(dev);
    int            addr = *(int *)addrp;

    if (client && client->addr == addr)
        return -EBUSY;
    return 0;
}

/* walk up mux tree */
static int i2c_check_mux_parents(struct i2c_adapter *adapter, int addr)
{
    struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
    int result;

    result = device_for_each_child(&adapter->dev, &addr,
                    __i2c_check_addr_busy);

    if (!result && parent)
        result = i2c_check_mux_parents(parent, addr);

    return result;
}

/* recurse down mux tree */
static int i2c_check_mux_children(struct device *dev, void *addrp)
{
    int result;

    if (dev->type == &i2c_adapter_type)
        result = device_for_each_child(dev, addrp,
                        i2c_check_mux_children);
    else
        result = __i2c_check_addr_busy(dev, addrp);

    return result;
}

static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr)
{
    struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
    int result = 0;

    if (parent)
        result = i2c_check_mux_parents(parent, addr);

    if (!result)
        result = device_for_each_child(&adapter->dev, &addr,
                        i2c_check_mux_children);

    return result;
}

/**
* i2c_lock_adapter - Get exclusive access to an I2C bus segment
* @adapter: Target I2C bus segment
*/
void i2c_lock_adapter(struct i2c_adapter *adapter)
{
    struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);

    if (parent)
        i2c_lock_adapter(parent);
    else
        rt_mutex_lock(&adapter->bus_lock);
}
EXPORT_SYMBOL_GPL(i2c_lock_adapter);

/**
* i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment
* @adapter: Target I2C bus segment
*/
static int i2c_trylock_adapter(struct i2c_adapter *adapter)
{
    struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);

    if (parent)
        return i2c_trylock_adapter(parent);
    else
        return rt_mutex_trylock(&adapter->bus_lock);
}

/**
* i2c_unlock_adapter - Release exclusive access to an I2C bus segment
* @adapter: Target I2C bus segment
*/
void i2c_unlock_adapter(struct i2c_adapter *adapter)
{
    struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);

    if (parent)
        i2c_unlock_adapter(parent);
    else
        rt_mutex_unlock(&adapter->bus_lock);
}
EXPORT_SYMBOL_GPL(i2c_unlock_adapter);

/**
* i2c_new_device - instantiate an i2c device
* @adap: the adapter managing the device
* @info: describes one I2C device; bus_num is ignored
* Context: can sleep
*
* Create an i2c device. Binding is handled through driver model
* probe()/remove() methods.  A driver may be bound to this device when we
* return from this function, or any later moment (e.g. maybe hotplugging will
* load the driver module).  This call is not appropriate for use by mainboard
* initialization logic, which usually runs during an arch_initcall() long
* before any i2c_adapter could exist.
*
* This returns the new i2c client, which may be saved for later use with
* i2c_unregister_device(); or NULL to indicate an error.
*/
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client    *client;
    int            status;

    client = kzalloc(sizeof *client, GFP_KERNEL);
    if (!client)
        return NULL;

    client->adapter = adap;

    client->dev.platform_data = info->platform_data;

    if (info->archdata)
        client->dev.archdata = *info->archdata;

    client->flags = info->flags;
    client->addr = info->addr;
    client->irq = info->irq;
    client->udelay = info->udelay;  // add by kfx

    strlcpy(client->name, info->type, sizeof(client->name));

    /* Check for address validity */
    status = i2c_check_client_addr_validity(client);
    if (status) {
        dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
            client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
        goto out_err_silent;
    }

    /* Check for address business */
    #if 0
    status = i2c_check_addr_busy(adap, client->addr);
    if (status)
        goto out_err;
    #else
    /* ddl@rock-chips.com : Devices which have some i2c addr can work in same i2c bus,
       if devices havn't work at the same time.*/
    status = i2c_check_addr_ex(adap, client->addr);
    if (status != 0)
        dev_err(&adap->dev, "%d i2c clients have been registered at 0x%02x",
            status, client->addr);   
    #endif

    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;
    client->dev.type = &i2c_client_type;
    client->dev.of_node = info->of_node;

    /* ddl@rock-chips.com : Devices which have some i2c addr can work in same i2c bus,
      if devices havn't work at the same time.*/
    #if 0
    dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
                 client->addr);
    #else
    if (status == 0)
        dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
                 client->addr);
    else
        dev_set_name(&client->dev, "%d-%04x-%01x", i2c_adapter_id(adap),
                 client->addr,status);
    #endif
   
    status = device_register(&client->dev);
    if (status)
        goto out_err;

    dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
        client->name, dev_name(&client->dev));

    return client;

out_err:
    dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
        "(%d)\n", client->name, client->addr, status);
out_err_silent:
    kfree(client);
    return NULL;
}
EXPORT_SYMBOL_GPL(i2c_new_device);

#ifdef CONFIG_PLAT_RK
#define RK610_KEY       "rk610"
static int __i2c_client_print(struct device *dev, void *param)
{
        struct i2c_client *client = i2c_verify_client(dev);

        if(client)
                printk(KERN_WARNING "client: %s, addr: 0x%x\n", client->name, client->addr);
        return 0;
}
static int __i2c_check_rk610_ex(struct device *dev, void *ex)
{
        struct i2c_client *client = i2c_verify_client(dev);

        if(!client)
                return 0;

        if(strstr(client->name, RK610_KEY) != NULL)
                *(int *)ex += 1 << 8;
        else
                *(int *)ex += 1;
        return 0;
}
int i2c_check_rk610_ex(int nr)
{
        int ex = 0, rk610_ex = 0, oth_ex = 0;
        struct i2c_adapter *adap = i2c_get_adapter(nr);

        if(!adap){
                printk(KERN_ERR "%s: adap(%d) is not exist\n", __func__, nr);
                return -EINVAL;
        }
        device_for_each_child(&adap->dev, &ex, __i2c_check_rk610_ex);

        if(ex & (1 << 8))
                rk610_ex = 1;

        oth_ex = ex & 0xff;

        if(rk610_ex && oth_ex){
                ex = 1;
                printk(KERN_WARNING "******************* WARNING ********************\n");
                dev_warn(&adap->dev, "%s is exist, clients:\n", RK610_KEY);
                device_for_each_child(&adap->dev, NULL, __i2c_client_print);
                printk(KERN_WARNING "************************************************\n");
        }
        else
                ex = 0;
        return ex;
}
#else
int i2c_check_rk610_ex(int nr)
{
        return 0;
}
#endif //end of CONFIG_PLAT_RK
EXPORT_SYMBOL_GPL(i2c_check_rk610_ex);

#ifdef CONFIG_I2C_RK30
int i2c_add_device(int nr, struct i2c_board_info const *info)
{
    int            status;
    struct i2c_client    *client;
        struct i2c_adapter *adap = i2c_get_adapter(nr);
        
        if(!adap){
                printk(KERN_ERR "%s: adap(%d) is not exist\n", __func__, nr);
                return -EINVAL;
        }

    client = kzalloc(sizeof *client, GFP_KERNEL);
    if (!client){
                dev_err(&adap->dev, "no memory for client\n");
        return -ENOMEM;
        }

    client->adapter = adap;

    client->dev.platform_data = info->platform_data;

    if (info->archdata)
        client->dev.archdata = *info->archdata;

    client->flags = info->flags;
    client->addr = info->addr;
    client->irq = info->irq;
    client->udelay = info->udelay;  // add by kfx

    strlcpy(client->name, info->type, sizeof(client->name));

    /* Check for address validity */
    status = i2c_check_client_addr_validity(client);
    if (status) {
        dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
            client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
        goto out_err_silent;
    }

    /* Check for address business */
    status = i2c_check_addr_busy(adap, client->addr);
    if (status){
                status = -EEXIST;
        dev_warn(&adap->dev, "i2c clients have been registered at 0x%02x\n", client->addr);   
        goto out_err_silent;
        }

    int i;

    for (i = 0; i < count; i++)
        crc = crc8((crc ^ p) << 8);
    return crc;
}

/* Assume a 7-bit address, which is reasonable for SMBus */
static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg)
{
    /* The address will be sent first */
    u8 addr = (msg->addr << 1) | !!(msg->flags & I2C_M_RD);
    pec = i2c_smbus_pec(pec, &addr, 1);

    /* The data buffer follows */
    return i2c_smbus_pec(pec, msg->buf, msg->len);
}

/* Used for write only transactions */
static inline void i2c_smbus_add_pec(struct i2c_msg *msg)
{
    msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg);
    msg->len++;
}

/* Return <0 on CRC error
   If there was a write before this read (most cases) we need to take the
   partial CRC from the write part into account.
   Note that this function does modify the message (we need to decrease the
   message length to hide the CRC byte from the caller). */
static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg)
{
    u8 rpec = msg->buf[--msg->len];
    cpec = i2c_smbus_msg_pec(cpec, msg);

    if (rpec != cpec) {
        pr_debug("i2c-core: Bad PEC 0x%02x vs. 0x%02x\n",
            rpec, cpec);
        return -EBADMSG;
    }
    return 0;
}

/**
* i2c_smbus_read_byte - SMBus "receive byte" protocol
* @client: Handle to slave device
*
* This executes the SMBus "receive byte" protocol, returning negative errno
* else the byte received from the device.
*/
s32 i2c_smbus_read_byte(const struct i2c_client *client)
{
    union i2c_smbus_data data;
    int status;

    status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
                I2C_SMBUS_READ, 0,
                I2C_SMBUS_BYTE, &data);
    return (status < 0) ? status : data.byte;
}
EXPORT_SYMBOL(i2c_smbus_read_byte);

/**
* i2c_smbus_write_byte - SMBus "send byte" protocol
* @client: Handle to slave device
* @value: Byte to be sent
*
* This executes the SMBus "send byte" protocol, returning negative errno
* else zero on success.
*/
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
{
    return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
                          I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
}
EXPORT_SYMBOL(i2c_smbus_write_byte);

/**
* i2c_smbus_read_byte_data - SMBus "read byte" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
*
* This executes the SMBus "read byte" protocol, returning negative errno
* else a data byte received from the device.
*/
s
回复

使用道具 举报

该用户从未签到

3

主题

2324

回帖

1万

积分

三级逆天

积分
13263

终身成就奖社区居民忠实会员社区劳模最爱沙发优秀斑竹奖

QQ
发表于 2015-7-7 09:55:40 | 显示全部楼层
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

论坛开启做任务可以
额外奖励金币快速赚
积分升级了


Copyright ©2011-2024 NTpcb.com All Right Reserved.  Powered by Discuz! (NTpcb)

本站信息均由会员发表,不代表NTpcb立场,如侵犯了您的权利请发帖投诉

平平安安
TOP
快速回复 返回顶部 返回列表