原文地址:https://blog.youkuaiyun.com/lsn946803746/article/details/52515225
BLSP(BAM Low-Speed Peripheral) , 每一个BLSP含有两个QUP, 每一个QUP可以被配置为I2C, SPI, UART, UIM接口, BLSP是高通对于低速接口的一种管理方式。
i2c@f9923000 { /* BLSP-1 QUP-1 */
cell-index = <1>;
compatible = "qcom,i2c-qup";
#address-cells = <1>;
#size-cells = <0>;
reg-names = "qup_phys_addr";
reg = <0xf9923000 0x1000>;
interrupt-names = "qup_err_intr";
interrupts = <0 95 0>;
qcom,i2c-bus-freq = <100000>;
qcom,i2c-src-freq = <19200000>;
qcom,sda-gpio = <&msmgpio 2 0>;
qcom,scl-gpio = <&msmgpio 3 0>;
qcom,master-id = <86>;
};
i2c_cdc: i2c@f9927000 { /* BLSP1 QUP5 */
cell-index = <5>;
compatible = "qcom,i2c-qup";
#address-cells = <1>;
#size-cells = <0>;
reg-names = "qup_phys_addr";
reg = <0xf9927000 0x1000>;
interrupt-names = "qup_err_intr";
interrupts = <0 99 0>;
qcom,i2c-bus-freq = <100000>;
qcom,i2c-src-freq = <19200000>;
qcom,master-id = <86>;
};
i2c: i2c@f9928000 { /* BLSP1 QUP6 */
cell-index = <6>;
compatible = "qcom,i2c-qup";
#address-cells = <1>;
#size-cells = <0>;
reg-names = "qup_phys_addr";
reg = <0xf9928000 0x1000>;
interrupt-names = "qup_err_intr";
interrupts = <0 100 0>;
qcom,i2c-bus-freq = <100000>;
qcom,i2c-src-freq = <19200000>;
qcom,sda-gpio = <&msmgpio 16 0>;
qcom,scl-gpio = <&msmgpio 17 0>;
qcom,master-id = <86>;
};
i2c@f9924000 { /* BLSP-1 QUP-3 */
cell-index = <2>;
compatible = "qcom,i2c-qup";
#address-cells = <1>;
#size-cells = <0>;
reg-names = "qup_phys_addr";
reg = <0xf9924000 0x1000>;
interrupt-names = "qup_err_intr";
interrupts = <0 96 0>;
qcom,i2c-bus-freq = <100000>;
qcom,i2c-src-freq = <19200000>;
qcom,sda-gpio = <&msmgpio 8 0>;
qcom,scl-gpio = <&msmgpio 9 0>;
qcom,master-id = <86>;
};
i2c@f9925000 { /* BLSP-1 QUP-3 */
cell-index = <0>;
compatible = "qcom,i2c-qup";
#address-cells = <1>;
#size-cells = <0>;
reg-names = "qup_phys_addr";
reg = <0xf9925000 0x1000>;
interrupt-names = "qup_err_intr";
interrupts = <0 97 0>;
qcom,i2c-bus-freq = <100000>;
qcom,i2c-src-freq = <19200000>;
qcom,sda-gpio = <&msmgpio 10 0>;
qcom,scl-gpio = <&msmgpio 11 0>;
qcom,clk-ctl-xfer;
qcom,master-id = <86>;
};
以上是I2C的硬件接口描述。
i2c@f9923000{ //I2C设备挂的两种设备 focaltech goodix
focaltech@38{
compatible = "focaltech,5x06";
reg = <0x38>;
interrupt-parent = <&msmgpio>;
interrupts = <1 0x2008>;
vdd-supply = <&pm8110_l19>;
vcc_i2c-supply = <&pm8110_l14>;
focaltech,name = "ft5x06";
focaltech,family-id = <0x54>;
focaltech,reset-gpio = <&msmgpio 0 0x00>;
focaltech,irq-gpio = <&msmgpio 1 0x00>;
focaltech,display-coords = <0 0 480 854>;
focaltech,button-map= <139 172 158>;
focaltech,no-force-update;
focaltech,i2c-pull-up;
focaltech,group-id = <1>;
focaltech,hard-reset-delay-ms = <20>;
focaltech,soft-reset-delay-ms = <150>;
focaltech,num-max-touches = <10>;
focaltech,fw-name = "5436_Ref_Asus89118_V12_D01_20160712_app.i";
focaltech,fw-delay-aa-ms = <50>;
focaltech,fw-delay-55-ms = <30>;
focaltech,fw-upgrade-id1 = <0x79>;
focaltech,fw-upgrade-id2 = <0x03>;
focaltech,fw-delay-readid-ms = <10>;
focaltech,fw-delay-era-flsh-ms = <2000>;
};
goodix@5d {
compatible = "goodix,gt915l"; //用于.of_match_table = goodix_match_table,
reg = <0x5d>; //地址
interrupt-parent = <&msmgpio>; //中断块
interrupts = <1 0x2008>; //中断地址
vdd_ana-supply = <&pm8110_l19>;
vcc_i2c-supply = <&pm8110_l14>;
goodix,rst-gpio = <&msmgpio 0 0x00>; //复位引脚
goodix,irq-gpio = <&msmgpio 1 0x00>; //中断引脚
//goodix,panel-coords = <0 0 540 980>;
//goodix,display-coords = <0 0 540 960>;
//goodix,button-map= <139 172 158>;
//goodix,product-id = "915L";
//goodix,enable-power-off;
goodix,cfg-group0 = [
47 E0 01 56 03 05 34 01 01 05
28 08 50 32 03 05 00 00 FF 7F
00 11 05 17 18 23 14 8C 2E 0E
35 33 0F 0A 00 00 01 BA 33 1D
00 01 00 00 00 00 00 0A 10 00
2B 2E 55 94 C5 02 00 00 00 04
83 31 00 79 37 00 70 3E 00 68
46 00 61 50 00 61 18 38 60 00
F0 4A 3A EE EE 27 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 0F 19 00 00
4B 37 0E 10 12 14 16 18 1A 1C
02 04 06 08 0A 0C 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 2A 29 28 26 24 22 21 20
1F 1E 1D 1C 16 18 00 02 04 06
08 0A 0C 0F 10 12 13 14 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 4C 01];
goodix,cfg-group2 = [
48 D0 02 00 05 05 75 01 01 0F 24
0F 64 3C 03 05 00 00 00 02 00 00
00 16 19 1C 14 8C 0E 0E 24 00 31
0D 00 00 00 83 33 1D 00 41 00 00
3C 0A 14 08 0A 00 2B 1C 3C 94 D5
03 08 00 00 04 93 1E 00 82 23 00
74 29 00 69 2F 00 5F 37 00 5F 20
40 60 00 F0 40 30 55 50 27 00 00
00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 14 19 00 00
50 50 02 04 06 08 0A 0C 0E 10 12
14 16 18 1A 1C 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 1D
1E 1F 20 21 22 24 26 28 29 2A 1C
18 16 14 13 12 10 0F 0C 0A 08 06
04 02 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 3C 01];
};
};
I2C bus 总线的构建。
在kernel/include/linux/init.h中
#define arch_initcall(fn)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
1
2
3
4
将i2c-bus的driver init函数放到.initcall3.init 代码段中。
之所以,i2c-bus的driver和i2c 设备的驱动init函数使用的不同的函数放到.initcall代码段,主要原因是后面调用.initcall中的函数执行顺序是按.initcall段的函数顺序进行的(按照initcall的level从0到7依次存放的)。
#define INIT_CALLS_LEVEL(level) \
VMLINUX_SYMBOL(__initcall##level##_start) = .; \
*(.initcall##level##.init) \
*(.initcall##level##s.init)
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
*(.initcallearly.init) \
INIT_CALLS_LEVEL(0) \
INIT_CALLS_LEVEL(1) \
INIT_CALLS_LEVEL(2) \
INIT_CALLS_LEVEL(3) \
INIT_CALLS_LEVEL(4) \
INIT_CALLS_LEVEL(5) \
INIT_CALLS_LEVEL(rootfs) \
INIT_CALLS_LEVEL(6) \
INIT_CALLS_LEVEL(7) \
VMLINUX_SYMBOL(__initcall_end) = .;
如module_init()的驱动初始化是等级66
(kernel/include/linux/init.h)
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define __define_initcall(level,fn,id) /
static initcall_t __initcall_##fn##id __used /
__attribute__((__section__(".initcall" level ".init"))) = fn
initcall_t(typedef int (*initcall_t)(void))
1
这就保证了I2C总线的会提前的注册好,在注册挂载的驱动和设备。
在linux /init/main.c 中,
static void __init do_initcalls(void)
{
initcall_t *call;
call = &__initcall_start;
do {
(*call)();
call++;
} while (call < &__initcall_end);
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_tasks();
}
通过do_initcalls就可以把__initcall section段的函数调用起來。
kernel/drivers/i2c/busses/i2c-qup.c
/* Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*
*/
/*
* QUP driver for Qualcomm MSM platforms
*
*/
/* #define DEBUG */
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c/i2c-qup.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_i2c.h>
#include <linux/of_gpio.h>
#include <mach/board.h>
#include <mach/gpiomux.h>
#include <mach/msm_bus_board.h>
MODULE_LICENSE("GPL v2");
MODULE_VERSION("0.2");
MODULE_ALIAS("platform:i2c_qup");
/* QUP Registers */
enum {
QUP_CONFIG = 0x0,
QUP_STATE = 0x4,
QUP_IO_MODE = 0x8,
QUP_SW_RESET = 0xC,
QUP_OPERATIONAL = 0x18,
QUP_ERROR_FLAGS = 0x1C,
QUP_ERROR_FLAGS_EN = 0x20,
QUP_MX_READ_CNT = 0x208,
QUP_MX_INPUT_CNT = 0x200,
QUP_MX_WR_CNT = 0x100,
QUP_OUT_DEBUG = 0x108,
QUP_OUT_FIFO_CNT = 0x10C,
QUP_OUT_FIFO_BASE = 0x110,
QUP_IN_READ_CUR = 0x20C,
QUP_IN_DEBUG = 0x210,
QUP_IN_FIFO_CNT = 0x214,
QUP_IN_FIFO_BASE = 0x218,
QUP_I2C_CLK_CTL = 0x400,
QUP_I2C_STATUS = 0x404,
};
/* QUP States and reset values */
enum {
QUP_RESET_STATE = 0,
QUP_RUN_STATE = 1U,
QUP_STATE_MASK = 3U,
QUP_PAUSE_STATE = 3U,
QUP_STATE_VALID = 1U << 2,
QUP_I2C_MAST_GEN = 1U << 4,
QUP_OPERATIONAL_RESET = 0xFF0,
QUP_I2C_STATUS_RESET = 0xFFFFFC,
};
/* QUP OPERATIONAL FLAGS */
enum {
QUP_OUT_SVC_FLAG = 1U << 8,
QUP_IN_SVC_FLAG = 1U << 9,
QUP_MX_INPUT_DONE = 1U << 11,
};
/* QUP_CONFIG values and flags */
enum {
I2C_MINI_CORE = 2U << 8,
I2C_N_VAL = 0xF,
I2C_CORE_CLK_ON_EN = BIT(13),
};
/* Packing Unpacking words in FIFOs , and IO modes*/
enum {
QUP_WR_BLK_MODE = 1U << 10,
QUP_RD_BLK_MODE = 1U << 12,
QUP_UNPACK_EN = 1U << 14,
QUP_PACK_EN = 1U << 15,
};
/* QUP tags */
enum {
QUP_OUT_NOP = 0,
QUP_OUT_START = 1U << 8,
QUP_OUT_DATA = 2U << 8,
QUP_OUT_STOP = 3U << 8,
QUP_OUT_REC = 4U << 8,
QUP_IN_DATA = 5U << 8,
QUP_IN_STOP = 6U << 8,
QUP_IN_NACK = 7U << 8,
};
/* Status, Error flags */
enum {
I2C_STATUS_WR_BUFFER_FULL = 1U << 0,
I2C_STATUS_BUS_ACTIVE = 1U << 8,
I2C_STATUS_BUS_MASTER = 1U << 9,
I2C_STATUS_ERROR_MASK = 0x38000FC,
QUP_I2C_NACK_FLAG = 1U << 3,
QUP_IN_NOT_EMPTY = 1U << 5,
QUP_STATUS_ERROR_FLAGS = 0x7C,
};
/* Master status clock states */
enum {
I2C_CLK_RESET_BUSIDLE_STATE = 0,
I2C_CLK_FORCED_LOW_STATE = 5,
};
enum msm_i2c_state {
MSM_I2C_PM_ACTIVE,
MSM_I2C_PM_SUSPENDED,
MSM_I2C_SYS_SUSPENDING,
MSM_I2C_SYS_SUSPENDED,
};
#define QUP_MAX_CLK_STATE_RETRIES 300
#define DEFAULT_CLK_RATE (19200000)
#define I2C_STATUS_CLK_STATE 13
#define QUP_OUT_FIFO_NOT_EMPTY 0x10
#define I2C_GPIOS_DT_CNT (2) /* sda and scl */
static char const * const i2c_rsrcs[] = {"i2c_clk", "i2c_sda"};
static struct gpiomux_setting recovery_config = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_8MA,
.pull = GPIOMUX_PULL_NONE,
};
/**
* qup_i2c_clk_path_vote: data to use bus scaling driver for clock path vote
*
* @client_hdl when zero, client is not registered with the bus scaling driver,
* and bus scaling functionality should not be used. When non zero, it
* is a bus scaling client id and may be used to vote for clock path.
* @reg_err when true, registration error was detected and an error message was
* logged. i2c will attempt to re-register but will log error only once.
* once registration succeed, the flag is set to false.
*/
struct qup_i2c_clk_path_vote {
u32 client_hdl;
struct msm_bus_scale_pdata *pdata;
bool reg_err;
};
struct qup_i2c_dev {
struct device *dev;
void __iomem *base; /* virtual */
void __iomem *gsbi; /* virtual */
int in_irq;
int out_irq;
int err_irq;
int num_irqs;
struct clk *clk;
struct clk *pclk;
struct i2c_adapter adapter;
struct i2c_msg *msg;
int pos;
int cnt;
int err;
int mode;
int clk_ctl;
int one_bit_t;
int out_fifo_sz;
int in_fifo_sz;
int out_blk_sz;
int in_blk_sz;
int wr_sz;
struct msm_i2c_platform_data *pdata;
enum msm_i2c_state pwr_state;
atomic_t xfer_progress;
struct mutex mlock;
void *complete;
int i2c_gpios[ARRAY_SIZE(i2c_rsrcs)];
struct qup_i2c_clk_path_vote clk_path_vote;
};
#ifdef CONFIG_PM
static int i2c_qup_pm_resume_runtime(struct device *device);
#endif
#ifdef DEBUG
static void
qup_print_status(struct qup_i2c_dev *dev)
{
uint32_t val;
val = readl_relaxed(dev->base+QUP_CONFIG);
dev_dbg(dev->dev, "Qup config is :0x%x\n", val);
val = readl_relaxed(dev->base+QUP_STATE);
dev_dbg(dev->dev, "Qup state is :0x%x\n", val);
val = readl_relaxed(dev->base+QUP_IO_MODE);
dev_dbg(dev->dev, "Qup mode is :0x%x\n", val);
}
#else
static inline void qup_print_status(struct qup_i2c_dev *dev)
{
}
#endif
static irqreturn_t
qup_i2c_interrupt(int irq, void *devid)
{
struct qup_i2c_dev *dev = devid;
uint32_t status = 0;
uint32_t status1 = 0;
uint32_t op_flgs = 0;
int err = 0;
if (atomic_read(&dev->xfer_progress) != 1) {
dev_err(dev->dev, "irq:%d when PM suspended\n", irq);
return IRQ_NONE;
}
status = readl_relaxed(dev->base + QUP_I2C_STATUS);
status1 = readl_relaxed(dev->base + QUP_ERROR_FLAGS);
op_flgs = readl_relaxed(dev->base + QUP_OPERATIONAL);
if (!dev->msg || !dev->complete) {
/* Clear Error interrupt if it's a level triggered interrupt*/
if (dev->num_irqs == 1) {
writel_relaxed(QUP_RESET_STATE, dev->base+QUP_STATE);
/* Ensure that state is written before ISR exits */
mb();
}
return IRQ_HANDLED;
}
if (status & I2C_STATUS_ERROR_MASK) {
dev_err(dev->dev, "QUP: I2C status flags :0x%x, irq:%d\n",
status, irq);
err = status;
/* Clear Error interrupt if it's a level triggered interrupt*/
if (dev->num_irqs == 1) {
writel_relaxed(QUP_RESET_STATE, dev->base+QUP_STATE);
/* Ensure that state is written before ISR exits */
mb();
}
goto intr_done;
}
if (status1 & 0x7F) {
dev_err(dev->dev, "QUP: QUP status flags :0x%x\n", status1);
err = -status1;
/* Clear Error interrupt if it's a level triggered interrupt*/
if (dev->num_irqs == 1) {
writel_relaxed((status1 & QUP_STATUS_ERROR_FLAGS),
dev->base + QUP_ERROR_FLAGS);
/* Ensure that error flags are cleared before ISR
* exits
*/
mb();
}
goto intr_done;
}
if ((dev->num_irqs == 3) && (dev->msg->flags == I2C_M_RD)
&& (irq == dev->out_irq))
return IRQ_HANDLED;
if (op_flgs & QUP_OUT_SVC_FLAG) {
writel_relaxed(QUP_OUT_SVC_FLAG, dev->base + QUP_OPERATIONAL);
/* Ensure that service flag is acknowledged before ISR exits */
mb();
}
if (dev->msg->flags == I2C_M_RD) {
if ((op_flgs & QUP_MX_INPUT_DONE) ||
(op_flgs & QUP_IN_SVC_FLAG)) {
writel_relaxed(QUP_IN_SVC_FLAG, dev->base
+ QUP_OPERATIONAL);
/* Ensure that service flag is acknowledged before ISR
* exits
*/
mb();
} else
return IRQ_HANDLED;
}
intr_done:
dev_dbg(dev->dev, "QUP intr= %d, i2c status=0x%x, qup status = 0x%x\n",
irq, status, status1);
qup_print_status(dev);
dev->err = err;
complete(dev->complete);
return IRQ_HANDLED;
}
static int
qup_i2c_poll_state(struct qup_i2c_dev *dev, uint32_t req_state, bool only_valid)
{
uint32_t retries = 0;
dev_dbg(dev->dev, "Polling for state:0x%x, or valid-only:%d\n",
req_state, only_valid);
while (retries != 2000) {
uint32_t status = readl_relaxed(dev->base + QUP_STATE);
/*
* If only valid bit needs to be checked, requested state is
* 'don't care'
*/
if (status & QUP_STATE_VALID) {
if (only_valid)
return 0;
else if ((req_state & QUP_I2C_MAST_GEN) &&
(status & QUP_I2C_MAST_GEN))
return 0;
else if ((status & QUP_STATE_MASK) == req_state)
return 0;
}
if (retries++ == 1000)
udelay(100);
}
return -ETIMEDOUT;
}
static int
qup_update_state(struct qup_i2c_dev *dev, uint32_t state)
{
if (qup_i2c_poll_state(dev, 0, true) != 0)
return -EIO;
writel_relaxed(state, dev->base + QUP_STATE);
if (qup_i2c_poll_state(dev, state, false) != 0)
return -EIO;
return 0;
}
#define MSM_I2C_CLK_PATH_SUSPEND (0)
#define MSM_I2C_CLK_PATH_RESUME (1)
#define MSM_I2C_CLK_PATH_MAX_BW(dev) ((dev->pdata->src_clk_rate * 8) / 1000)
static int i2c_qup_clk_path_init(struct platform_device *pdev,
struct qup_i2c_dev *dev)
{
struct msm_bus_vectors *paths = NULL;
struct msm_bus_paths *usecases = NULL;
if (!dev->pdata->master_id)
return 0;
dev_dbg(&pdev->dev, "initialises bus-scaling clock voting");
paths = devm_kzalloc(&pdev->dev, sizeof(*paths) * 2, GFP_KERNEL);
if (!paths) {
dev_err(&pdev->dev,
"msm_bus_paths.paths memory allocation failed");
return -ENOMEM;
}
usecases = devm_kzalloc(&pdev->dev, sizeof(*usecases) * 2, GFP_KERNEL);
if (!usecases) {
dev_err(&pdev->dev,
"msm_bus_scale_pdata.usecases memory allocation failed");
goto path_init_err;
}
dev->clk_path_vote.pdata = devm_kzalloc(&pdev->dev,
sizeof(*dev->clk_path_vote.pdata),
GFP_KERNEL);
if (!dev->clk_path_vote.pdata) {
dev_err(&pdev->dev,
"msm_bus_scale_pdata memory allocation failed");
goto path_init_err;
}
paths[MSM_I2C_CLK_PATH_SUSPEND] = (struct msm_bus_vectors) {
dev->pdata->master_id, MSM_BUS_SLAVE_EBI_CH0, 0, 0
};
paths[MSM_I2C_CLK_PATH_RESUME] = (struct msm_bus_vectors) {
dev->pdata->master_id, MSM_BUS_SLAVE_EBI_CH0, 0,
MSM_I2C_CLK_PATH_MAX_BW(dev)
};
usecases[MSM_I2C_CLK_PATH_SUSPEND] = (struct msm_bus_paths) {
.num_paths = 1,
.vectors = &paths[MSM_I2C_CLK_PATH_SUSPEND],
};
usecases[MSM_I2C_CLK_PATH_RESUME] = (struct msm_bus_paths) {
.num_paths = 1,
.vectors = &paths[MSM_I2C_CLK_PATH_RESUME],
};
*dev->clk_path_vote.pdata = (struct msm_bus_scale_pdata) {
.active_only = dev->pdata->active_only,
.name = pdev->name,
.num_usecases = 2,
.usecase = usecases,
};
return 0;
path_init_err:
devm_kfree(&pdev->dev, paths);
devm_kfree(&pdev->dev, usecases);
devm_kfree(&pdev->dev, dev->clk_path_vote.pdata);
dev->clk_path_vote.pdata = NULL;
return -ENOMEM;
}
static void i2c_qup_clk_path_teardown(struct qup_i2c_dev *dev)
{
if (dev->clk_path_vote.client_hdl) {
msm_bus_scale_unregister_client(dev->clk_path_vote.client_hdl);
dev->clk_path_vote.client_hdl = 0;
}
}
static void i2c_qup_clk_path_vote(struct qup_i2c_dev *dev)
{
if (dev->clk_path_vote.client_hdl)
msm_bus_scale_client_update_request(
dev->clk_path_vote.client_hdl,
MSM_I2C_CLK_PATH_RESUME);
}
static void i2c_qup_clk_path_unvote(struct qup_i2c_dev *dev)
{
if (dev->clk_path_vote.client_hdl)
msm_bus_scale_client_update_request(
dev->clk_path_vote.client_hdl,
MSM_I2C_CLK_PATH_SUSPEND);
}
/**
* i2c_qup_clk_path_postponed_register: reg with bus-scaling after it is probed
*
* Workaround: i2c driver may be probed before the bus scaling driver. Thus,
* this function should be called not from probe but from a later context.
* This function may be called more then once before register succeed. At
* this case only one error message will be logged. At boot time all clocks
* are on, so earlier i2c transactions should succeed.
*/
static void i2c_qup_clk_path_postponed_register(struct qup_i2c_dev *dev)
{
/*
* bail out if path voting is diabled (master_id == 0) or if it is
* already registered (client_hdl != 0)
*/
if (!dev->pdata->master_id || dev->clk_path_vote.client_hdl)
return;
dev->clk_path_vote.client_hdl = msm_bus_scale_register_client(
dev->clk_path_vote.pdata);
if (dev->clk_path_vote.client_hdl) {
if (dev->clk_path_vote.reg_err) {
/* log a success message if an error msg was logged */
dev->clk_path_vote.reg_err = false;
dev_info(dev->dev,
"msm_bus_scale_register_client(mstr-id:%d "
"actv-only:%d):0x%x",
dev->pdata->master_id, dev->pdata->active_only,
dev->clk_path_vote.client_hdl);
}
if (dev->pdata->active_only)
i2c_qup_clk_path_vote(dev);
} else {
/* guard to log only one error on multiple failure */
if (!dev->clk_path_vote.reg_err) {
dev->clk_path_vote.reg_err = true;
dev_info(dev->dev,
"msm_bus_scale_register_client(mstr-id:%d "
"actv-only:%d):0",
dev->pdata->master_id, dev->pdata->active_only);
}
}
}
static int i2c_qup_gpio_request(struct qup_i2c_dev *dev)
{
int i;
int result = 0;
for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
if (dev->i2c_gpios[i] >= 0) {
result = gpio_request(dev->i2c_gpios[i], i2c_rsrcs[i]);
if (result) {
dev_err(dev->dev,
"gpio_request for pin %d failed with error %d\n",
dev->i2c_gpios[i], result);
goto error;
}
}
}
return 0;
error:
for (; --i >= 0;) {
if (dev->i2c_gpios[i] >= 0)
gpio_free(dev->i2c_gpios[i]);
}
return result;
}
static void i2c_qup_gpio_free(struct qup_i2c_dev *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
if (dev->i2c_gpios[i] >= 0)
gpio_free(dev->i2c_gpios[i]);
}
}
static void i2c_qup_pm_suspend_clk(struct qup_i2c_dev *dev)
{
uint32_t status;
/* reset core and enable conditional dynamic clock gating */
qup_update_state(dev, QUP_RESET_STATE);
status = readl_relaxed(dev->base + QUP_CONFIG);
status |= I2C_CORE_CLK_ON_EN;
writel_relaxed(status, dev->base + QUP_CONFIG);
/* ensure that write has really gone through */
mb();
clk_disable_unprepare(dev->clk);
if (!dev->pdata->keep_ahb_clk_on)
clk_disable_unprepare(dev->pclk);
}
static void i2c_qup_pm_resume_clk(struct qup_i2c_dev *dev)
{
clk_prepare_enable(dev->clk);
if (!dev->pdata->keep_ahb_clk_on)
clk_prepare_enable(dev->pclk);
}
static void i2c_qup_pm_suspend(struct qup_i2c_dev *dev)
{
if (dev->pwr_state == MSM_I2C_PM_SUSPENDED) {
dev_err(dev->dev, "attempt to suspend when suspended\n");
return;
}
if (!dev->pdata->clk_ctl_xfer)
i2c_qup_pm_suspend_clk(dev);
if (!dev->pdata->active_only)
i2c_qup_clk_path_unvote(dev);
i2c_qup_gpio_free(dev);
dev->pwr_state = MSM_I2C_PM_SUSPENDED;
}
static void i2c_qup_pm_resume(struct qup_i2c_dev *dev)
{
if (dev->pwr_state == MSM_I2C_PM_ACTIVE)
return;
i2c_qup_gpio_request(dev);
i2c_qup_clk_path_postponed_register(dev);
if (!dev->pdata->active_only)
i2c_qup_clk_path_vote(dev);
if (!dev->pdata->clk_ctl_xfer)
i2c_qup_pm_resume_clk(dev);
dev->pwr_state = MSM_I2C_PM_ACTIVE;
}
static int
qup_i2c_poll_writeready(struct qup_i2c_dev *dev, int rem)
{
uint32_t retries = 0;
while (retries != 2000) {
uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
if (!(status & I2C_STATUS_WR_BUFFER_FULL)) {
if (((dev->msg->flags & I2C_M_RD) || (rem == 0)) &&
!(status & I2C_STATUS_BUS_ACTIVE))
return 0;
else if ((dev->msg->flags == 0) && (rem > 0))
return 0;
else /* 1-bit delay before we check for bus busy */
udelay(dev->one_bit_t);
}
if (retries++ == 1000) {
/*
* Wait for FIFO number of bytes to be absolutely sure
* that I2C write state machine is not idle. Each byte
* takes 9 clock cycles. (8 bits + 1 ack)
*/
usleep_range((dev->one_bit_t * (dev->out_fifo_sz * 9)),
(dev->one_bit_t * (dev->out_fifo_sz * 9)));
}
}
qup_print_status(dev);
return -ETIMEDOUT;
}
static int qup_i2c_poll_clock_ready(struct qup_i2c_dev *dev)
{
uint32_t retries = 0;
uint32_t op_flgs = -1, clk_state = -1;
/*
* Wait for the clock state to transition to either IDLE or FORCED
* LOW. This will usually happen within one cycle of the i2c clock.
*/
while (retries++ < QUP_MAX_CLK_STATE_RETRIES) {
uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
clk_state = (status >> I2C_STATUS_CLK_STATE) & 0x7;
/* Read the operational register */
op_flgs = readl_relaxed(dev->base +
QUP_OPERATIONAL) & QUP_OUT_FIFO_NOT_EMPTY;
/*
* In very corner case when slave do clock stretching and
* output fifo will have 1 block of data space empty at
* the same time. So i2c qup will get output service
* interrupt and as it doesn't have more data to be written.
* This can lead to issue where output fifo is not empty.
*/
if (op_flgs == 0 &&
(clk_state == I2C_CLK_RESET_BUSIDLE_STATE ||
clk_state == I2C_CLK_FORCED_LOW_STATE)){
dev_dbg(dev->dev, "clk_state 0x%x op_flgs [%x]\n",
clk_state, op_flgs);
return 0;
}
/* 1-bit delay before we check again */
udelay(dev->one_bit_t);
}
dev_err(dev->dev, "Error waiting for clk ready clk_state: 0x%x op_flgs: 0x%x\n",
clk_state, op_flgs);
return -ETIMEDOUT;
}
#ifdef DEBUG
static void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val,
uint32_t addr, int rdwr)
{
if (rdwr)
dev_dbg(dev->dev, "RD:Wrote 0x%x to out_ff:0x%x\n", val, addr);
else
dev_dbg(dev->dev, "WR:Wrote 0x%x to out_ff:0x%x\n", val, addr);
}
#else
static inline void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val,
uint32_t addr, int rdwr)
{
}
#endif
static void
qup_issue_read(struct qup_i2c_dev *dev, struct i2c_msg *msg, int *idx,
uint32_t carry_over)
{
uint16_t addr = (msg->addr << 1) | 1;
/* QUP limit 256 bytes per read. By HW design, 0 in the 8-bit field
* is treated as 256 byte read.
*/
uint16_t rd_len = ((dev->cnt == 256) ? 0 : dev->cnt);
if (*idx % 4) {
writel_relaxed(carry_over | ((QUP_OUT_START | addr) << 16),
dev->base + QUP_OUT_FIFO_BASE);/* + (*idx-2)); */
qup_verify_fifo(dev, carry_over |
((QUP_OUT_START | addr) << 16), (uint32_t)dev->base
+ QUP_OUT_FIFO_BASE + (*idx - 2), 1);
writel_relaxed((QUP_OUT_REC | rd_len),
dev->base + QUP_OUT_FIFO_BASE);/* + (*idx+2)); */
qup_verify_fifo(dev, (QUP_OUT_REC | rd_len),
(uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx + 2), 1);
} else {
writel_relaxed(((QUP_OUT_REC | rd_len) << 16)
| QUP_OUT_START | addr,
dev->base + QUP_OUT_FIFO_BASE);/* + (*idx)); */
qup_verify_fifo(dev, QUP_OUT_REC << 16 | rd_len << 16 |
QUP_OUT_START | addr,
(uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx), 1);
}
*idx += 4;
}
static void
qup_issue_write(struct qup_i2c_dev *dev, struct i2c_msg *msg, int rem,
int *idx, uint32_t *carry_over)
{
int entries = dev->cnt;
int empty_sl = dev->wr_sz - ((*idx) >> 1);
int i = 0;
uint32_t val = 0;
uint32_t last_entry = 0;
uint16_t addr = msg->addr << 1;
if (dev->pos == 0) {
if (*idx % 4) {
writel_relaxed(*carry_over | ((QUP_OUT_START |
addr) << 16),
dev->base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, *carry_over | QUP_OUT_START << 16 |
addr << 16, (uint32_t)dev->base +
QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
} else
val = QUP_OUT_START | addr;
*idx += 2;
i++;
entries++;
} else {
/* Avoid setp time issue by adding 1 NOP when number of bytes
* are more than FIFO/BLOCK size. setup time issue can't appear
* otherwise since next byte to be written will always be ready
*/
val = (QUP_OUT_NOP | 1);
*idx += 2;
i++;
entries++;
}
if (entries > empty_sl)
entries = empty_sl;
for (; i < (entries - 1); i++) {
if (*idx % 4) {
writel_relaxed(val | ((QUP_OUT_DATA |
msg->buf[dev->pos]) << 16),
dev->base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, val | QUP_OUT_DATA << 16 |
msg->buf[dev->pos] << 16, (uint32_t)dev->base +
QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
} else
val = QUP_OUT_DATA | msg->buf[dev->pos];
(*idx) += 2;
dev->pos++;
}
if (dev->pos < (msg->len - 1))
last_entry = QUP_OUT_DATA;
else if (rem > 1) /* not last array entry */
last_entry = QUP_OUT_DATA;
else
last_entry = QUP_OUT_STOP;
if ((*idx % 4) == 0) {
/*
* If read-start and read-command end up in different fifos, it
* may result in extra-byte being read due to extra-read cycle.
* Avoid that by inserting NOP as the last entry of fifo only
* if write command(s) leave 1 space in fifo.
*/
if (rem > 1) {
struct i2c_msg *next = msg + 1;
if (next->addr == msg->addr && (next->flags & I2C_M_RD)
&& *idx == ((dev->wr_sz*2) - 4)) {
writel_relaxed(((last_entry |
msg->buf[dev->pos]) |
((1 | QUP_OUT_NOP) << 16)), dev->base +
QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
qup_verify_fifo(dev,
((last_entry | msg->buf[dev->pos]) |
((1 | QUP_OUT_NOP) << 16)),
(uint32_t)dev->base +
QUP_OUT_FIFO_BASE + (*idx), 0);
*idx += 2;
} else if ((dev->pos == msg->len - 1)
&& *idx < (dev->wr_sz*2) &&
(next->addr != msg->addr)) {
/* Last byte of an intermittent write */
writel_relaxed((QUP_OUT_STOP |
msg->buf[dev->pos]),
dev->base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev,
QUP_OUT_STOP | msg->buf[dev->pos],
(uint32_t)dev->base +
QUP_OUT_FIFO_BASE + (*idx), 0);
*idx += 2;
} else
*carry_over = (last_entry | msg->buf[dev->pos]);
} else {
writel_relaxed((last_entry | msg->buf[dev->pos]),
dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
qup_verify_fifo(dev, last_entry | msg->buf[dev->pos],
(uint32_t)dev->base + QUP_OUT_FIFO_BASE +
(*idx), 0);
}
} else {
writel_relaxed(val | ((last_entry | msg->buf[dev->pos]) << 16),
dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
qup_verify_fifo(dev, val | (last_entry << 16) |
(msg->buf[dev->pos] << 16), (uint32_t)dev->base +
QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
}
*idx += 2;
dev->pos++;
dev->cnt = msg->len - dev->pos;
}
static void
qup_set_read_mode(struct qup_i2c_dev *dev, int rd_len)
{
uint32_t wr_mode = (dev->wr_sz < dev->out_fifo_sz) ?
QUP_WR_BLK_MODE : 0;
if (rd_len > 256) {
dev_dbg(dev->dev, "HW limit: Breaking reads in chunk of 256\n");
rd_len = 256;
}
if (rd_len <= dev->in_fifo_sz) {
writel_relaxed(wr_mode | QUP_PACK_EN | QUP_UNPACK_EN,
dev->base + QUP_IO_MODE);
writel_relaxed(rd_len, dev->base + QUP_MX_READ_CNT);
} else {
writel_relaxed(wr_mode | QUP_RD_BLK_MODE |
QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
writel_relaxed(rd_len, dev->base + QUP_MX_INPUT_CNT);
}
}
static int
qup_set_wr_mode(struct qup_i2c_dev *dev, int rem)
{
int total_len = 0;
int ret = 0;
int len = dev->msg->len;
struct i2c_msg *next = NULL;
if (rem > 1)
next = dev->msg + 1;
while (rem > 1 && next->flags == 0 && (next->addr == dev->msg->addr)) {
len += next->len + 1;
next = next + 1;
rem--;
}
if (len >= (dev->out_fifo_sz - 1)) {
total_len = len + 1 + (len/(dev->out_blk_sz-1));
writel_relaxed(QUP_WR_BLK_MODE | QUP_PACK_EN | QUP_UNPACK_EN,
dev->base + QUP_IO_MODE);
dev->wr_sz = dev->out_blk_sz;
} else
writel_relaxed(QUP_PACK_EN | QUP_UNPACK_EN,
dev->base + QUP_IO_MODE);
if (rem > 1) {
if (next->addr == dev->msg->addr &&
next->flags == I2C_M_RD) {
qup_set_read_mode(dev, next->len);
/* make sure read start & read command are in 1 blk */
if ((total_len % dev->out_blk_sz) ==
(dev->out_blk_sz - 1))
total_len += 3;
else
total_len += 2;
}
}
/* WRITE COUNT register valid/used only in block mode */
if (dev->wr_sz == dev->out_blk_sz)
writel_relaxed(total_len, dev->base + QUP_MX_WR_CNT);
return ret;
}
static void qup_i2c_recover_bus_busy(struct qup_i2c_dev *dev)
{
int i;
int gpio_clk;
int gpio_dat;
bool gpio_clk_status = false;
uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
struct gpiomux_setting old_gpio_setting[ARRAY_SIZE(i2c_rsrcs)];
if (dev->pdata->msm_i2c_config_gpio)
return;
if (!(status & (I2C_STATUS_BUS_ACTIVE)) ||
(status & (I2C_STATUS_BUS_MASTER)))
return;
gpio_clk = dev->i2c_gpios[0];
gpio_dat = dev->i2c_gpios[1];
if ((gpio_clk == -1) && (gpio_dat == -1)) {
dev_err(dev->dev, "Recovery failed due to undefined GPIO's\n");
return;
}
disable_irq(dev->err_irq);
for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
if (msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE,
&recovery_config, &old_gpio_setting[i])) {
dev_err(dev->dev, "GPIO pins have no active setting\n");
goto recovery_end;
}
}
dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n",
gpio_get_value(gpio_clk), gpio_get_value(gpio_dat));
for (i = 0; i < 9; i++) {
if (gpio_get_value(gpio_dat) && gpio_clk_status)
break;
gpio_direction_output(gpio_clk, 0);
udelay(5);
gpio_direction_output(gpio_dat, 0);
udelay(5);
gpio_direction_input(gpio_clk);
udelay(5);
if (!gpio_get_value(gpio_clk))
udelay(20);
if (!gpio_get_value(gpio_clk))
usleep_range(10000, 10000);
gpio_clk_status = gpio_get_value(gpio_clk);
gpio_direction_input(gpio_dat);
udelay(5);
}
/* Configure ALT funciton to QUP I2C*/
for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE,
&old_gpio_setting[i], NULL);
}
udelay(10);
status = readl_relaxed(dev->base + QUP_I2C_STATUS);
if (!(status & I2C_STATUS_BUS_ACTIVE)) {
dev_info(dev->dev, "Bus busy cleared after %d clock cycles, "
"status %x\n",
i, status);
goto recovery_end;
}
dev_warn(dev->dev, "Bus still busy, status %x\n", status);
recovery_end:
enable_irq(dev->err_irq);
}
static int
qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
DECLARE_COMPLETION_ONSTACK(complete);
struct qup_i2c_dev *dev = i2c_get_adapdata(adap);
int ret;
int rem = num;
long timeout;
int err;
/*
* If all slaves of this controller behave as expected, they will
* implement suspend and won't call any transaction if they are
* suspended. Since controller is its parent, controller's suspend
* will be called only AFTER alls slaves are suspended.
* However reality is differe and some slave don't implement suspend
* If a slave tries to initiate transfer when we are suspended,
* pm_runtime_enabled is set to false by system-pm.
* Make sure we return error when transaction is initiated while
* we are in suspended state
*/
mutex_lock(&dev->mlock);
if (dev->pwr_state >= MSM_I2C_SYS_SUSPENDING) {
dev_err(dev->dev,
"xfer not allowed when ctrl is suspended addr:0x%x\n",
msgs->addr);
mutex_unlock(&dev->mlock);
return -EIO;
}
if (!pm_runtime_enabled(dev->dev)) {
dev_dbg(dev->dev, "Runtime PM FEATURE is disabled\n");
i2c_qup_pm_resume(dev);
} else {
pm_runtime_get_sync(dev->dev);
}
if (dev->pdata->clk_ctl_xfer)
i2c_qup_pm_resume_clk(dev);
atomic_set(&dev->xfer_progress, 1);
/* Initialize QUP registers during first transfer */
if (dev->clk_ctl == 0) {
int fs_div;
int hs_div;
uint32_t fifo_reg;
if (dev->gsbi) {
writel_relaxed(0x2 << 4, dev->gsbi);
/* GSBI memory is not in the same 1K region as other
* QUP registers. mb() here ensures that the GSBI
* register is updated in correct order and that the
* write has gone through before programming QUP core
* registers
*/
mb();
}
fs_div = ((dev->pdata->src_clk_rate
/ dev->pdata->clk_freq) / 2) - 3;
hs_div = 3;
dev->clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
fifo_reg = readl_relaxed(dev->base + QUP_IO_MODE);
if (fifo_reg & 0x3)
dev->out_blk_sz = (fifo_reg & 0x3) * 16;
else
dev->out_blk_sz = 16;
if (fifo_reg & 0x60)
dev->in_blk_sz = ((fifo_reg & 0x60) >> 5) * 16;
else
dev->in_blk_sz = 16;
/*
* The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
* associated with each byte written/received
*/
dev->out_blk_sz /= 2;
dev->in_blk_sz /= 2;
dev->out_fifo_sz = dev->out_blk_sz *
(2 << ((fifo_reg & 0x1C) >> 2));
dev->in_fifo_sz = dev->in_blk_sz *
(2 << ((fifo_reg & 0x380) >> 7));
dev_dbg(dev->dev, "QUP IN:bl:%d, ff:%d, OUT:bl:%d, ff:%d\n",
dev->in_blk_sz, dev->in_fifo_sz,
dev->out_blk_sz, dev->out_fifo_sz);
}
writel_relaxed(1, dev->base + QUP_SW_RESET);
ret = qup_i2c_poll_state(dev, QUP_RESET_STATE, false);
if (ret) {
dev_err(dev->dev, "QUP Busy:Trying to recover\n");
goto out_err;
}
if (dev->num_irqs == 3) {
enable_irq(dev->in_irq);
enable_irq(dev->out_irq);
}
enable_irq(dev->err_irq);
/* Initialize QUP registers */
writel_relaxed(0, dev->base + QUP_CONFIG);
writel_relaxed(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL);
writel_relaxed(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN);
writel_relaxed(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG);
/* Initialize I2C mini core registers */
writel_relaxed(0, dev->base + QUP_I2C_CLK_CTL);
writel_relaxed(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS);
while (rem) {
bool filled = false;
dev->cnt = msgs->len - dev->pos;
dev->msg = msgs;
dev->wr_sz = dev->out_fifo_sz;
dev->err = 0;
dev->complete = &complete;
if (qup_i2c_poll_state(dev, QUP_I2C_MAST_GEN, false) != 0) {
ret = -EIO;
goto out_err;
}
qup_print_status(dev);
/* HW limits Read upto 256 bytes in 1 read without stop */
if (dev->msg->flags & I2C_M_RD) {
qup_set_read_mode(dev, dev->cnt);
if (dev->cnt > 256)
dev->cnt = 256;
} else {
ret = qup_set_wr_mode(dev, rem);
if (ret != 0)
goto out_err;
/* Don't fill block till we get interrupt */
if (dev->wr_sz == dev->out_blk_sz)
filled = true;
}
err = qup_update_state(dev, QUP_RUN_STATE);
if (err < 0) {
ret = err;
goto out_err;
}
qup_print_status(dev);
writel_relaxed(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL);
/* CLK_CTL register is not in the same 1K region as other QUP
* registers. Ensure that clock control is written before
* programming other QUP registers
*/
mb();
do {
int idx = 0;
uint32_t carry_over = 0;
/* Transition to PAUSE state only possible from RUN */
err = qup_update_state(dev, QUP_PAUSE_STATE);
if (err < 0) {
ret = err;
goto out_err;
}
qup_print_status(dev);
/* This operation is Write, check the next operation
* and decide mode
*/
while (filled == false) {
if ((msgs->flags & I2C_M_RD))
qup_issue_read(dev, msgs, &idx,
carry_over);
else if (!(msgs->flags & I2C_M_RD))
qup_issue_write(dev, msgs, rem, &idx,
&carry_over);
if (idx >= (dev->wr_sz << 1))
filled = true;
/* Start new message */
if (filled == false) {
if (msgs->flags & I2C_M_RD)
filled = true;
else if (rem > 1) {
/* Only combine operations with
* same address
*/
struct i2c_msg *next = msgs + 1;
if (next->addr != msgs->addr)
filled = true;
else {
rem--;
msgs++;
dev->msg = msgs;
dev->pos = 0;
dev->cnt = msgs->len;
if (msgs->len > 256)
dev->cnt = 256;
}
} else
filled = true;
}
}
err = qup_update_state(dev, QUP_RUN_STATE);
if (err < 0) {
ret = err;
goto out_err;
}
dev_dbg(dev->dev, "idx:%d, rem:%d, num:%d, mode:%d\n",
idx, rem, num, dev->mode);
qup_print_status(dev);
timeout = wait_for_completion_timeout(&complete,
msecs_to_jiffies(dev->out_fifo_sz));
if (!timeout) {
uint32_t istatus = readl_relaxed(dev->base +
QUP_I2C_STATUS);
uint32_t qstatus = readl_relaxed(dev->base +
QUP_ERROR_FLAGS);
uint32_t op_flgs = readl_relaxed(dev->base +
QUP_OPERATIONAL);
/*
* Dont wait for 1 sec if i2c sees the bus
* active and controller is not master.
* A slave has pulled line low. Try to recover
*/
if (!(istatus & I2C_STATUS_BUS_ACTIVE) ||
(istatus & I2C_STATUS_BUS_MASTER)) {
timeout =
wait_for_completion_timeout(&complete,
HZ);
if (timeout)
goto timeout_err;
}
qup_i2c_recover_bus_busy(dev);
dev_err(dev->dev,
"Transaction timed out, SL-AD = 0x%x\n",
dev->msg->addr);
dev_err(dev->dev, "I2C Status: %x\n", istatus);
dev_err(dev->dev, "QUP Status: %x\n", qstatus);
dev_err(dev->dev, "OP Flags: %x\n", op_flgs);
writel_relaxed(1, dev->base + QUP_SW_RESET);
/* Make sure that the write has gone through
* before returning from the function
*/
mb();
ret = -ETIMEDOUT;
goto out_err;
}
timeout_err:
if (dev->err) {
if (dev->err > 0 &&
dev->err & QUP_I2C_NACK_FLAG) {
dev_err(dev->dev,
"I2C slave addr:0x%x not connected\n",
dev->msg->addr);
dev->err = ENOTCONN;
} else if (dev->err < 0) {
dev_err(dev->dev,
"QUP data xfer error %d\n", dev->err);
ret = dev->err;
goto out_err;
} else if (dev->err > 0) {
/*
* ISR returns +ve error if error code
* is I2C related, e.g. unexpected start
* So you may call recover-bus-busy when
* this error happens
*/
qup_i2c_recover_bus_busy(dev);
}
ret = -dev->err;
goto out_err;
}
if (dev->msg->flags & I2C_M_RD) {
int i;
uint32_t dval = 0;
for (i = 0; dev->pos < dev->msg->len; i++,
dev->pos++) {
uint32_t rd_status =
readl_relaxed(dev->base
+ QUP_OPERATIONAL);
if (i % 2 == 0) {
if ((rd_status &
QUP_IN_NOT_EMPTY) == 0)
break;
dval = readl_relaxed(dev->base +
QUP_IN_FIFO_BASE);
dev->msg->buf[dev->pos] =
dval & 0xFF;
} else
dev->msg->buf[dev->pos] =
((dval & 0xFF0000) >>
16);
}
dev->cnt -= i;
} else
filled = false; /* refill output FIFO */
dev_dbg(dev->dev, "pos:%d, len:%d, cnt:%d\n",
dev->pos, msgs->len, dev->cnt);
} while (dev->cnt > 0);
if (dev->cnt == 0) {
if (msgs->len == dev->pos) {
rem--;
msgs++;
dev->pos = 0;
}
if (rem) {
err = qup_i2c_poll_clock_ready(dev);
if (err < 0) {
ret = err;
goto out_err;
}
err = qup_update_state(dev, QUP_RESET_STATE);
if (err < 0) {
ret = err;
goto out_err;
}
}
}
/* Wait for I2C bus to be idle */
ret = qup_i2c_poll_writeready(dev, rem);
if (ret) {
dev_err(dev->dev,
"Error waiting for write ready\n");
goto out_err;
}
}
ret = num;
out_err:
disable_irq(dev->err_irq);
if (dev->num_irqs == 3) {
disable_irq(dev->in_irq);
disable_irq(dev->out_irq);
}
dev->complete = NULL;
dev->msg = NULL;
dev->pos = 0;
dev->err = 0;
dev->cnt = 0;
if (dev->pdata->clk_ctl_xfer)
i2c_qup_pm_suspend_clk(dev);
atomic_set(&dev->xfer_progress, 0);
mutex_unlock(&dev->mlock);
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return ret;
}
enum msm_i2c_dt_entry_status {
DT_REQUIRED,
DT_SUGGESTED,
DT_OPTIONAL,
};
enum msm_i2c_dt_entry_type {
DT_U32,
DT_GPIO,
DT_BOOL,
};
struct msm_i2c_dt_to_pdata_map {
const char *dt_name;
void *ptr_data;
enum msm_i2c_dt_entry_status status;
enum msm_i2c_dt_entry_type type;
int default_val;
};
int __devinit msm_i2c_rsrcs_dt_to_pdata_map(struct platform_device *pdev,
struct msm_i2c_platform_data *pdata, int *gpios)
{
int ret, err = 0;
struct device_node *node = pdev->dev.of_node;
struct msm_i2c_dt_to_pdata_map *itr;
struct msm_i2c_dt_to_pdata_map map[] = {
{"qcom,i2c-bus-freq", &pdata->clk_freq, DT_REQUIRED, DT_U32, 0},
{"cell-index", &pdev->id, DT_REQUIRED, DT_U32, -1},
{"qcom,i2c-src-freq", &pdata->src_clk_rate, DT_SUGGESTED, DT_U32, 0},
{"qcom,master-id", &pdata->master_id, DT_SUGGESTED, DT_U32, 0},
{"qcom,scl-gpio", gpios, DT_OPTIONAL, DT_GPIO, -1},
{"qcom,sda-gpio", gpios + 1, DT_OPTIONAL, DT_GPIO, -1},
{"qcom,clk-ctl-xfer", &pdata->clk_ctl_xfer, DT_OPTIONAL, DT_BOOL, -1},
{"qcom,active-only", &pdata->active_only, DT_OPTIONAL, DT_BOOL, 0},
{NULL, NULL, 0, 0, 0},
};
for (itr = map; itr->dt_name ; ++itr) {
switch (itr->type) {
case DT_GPIO:
ret = of_get_named_gpio(node, itr->dt_name, 0);
if (ret >= 0) {
*((int *) itr->ptr_data) = ret;
ret = 0;
}
break;
case DT_U32:
ret = of_property_read_u32(node, itr->dt_name,
(u32 *) itr->ptr_data);
break;
case DT_BOOL:
*((bool *) itr->ptr_data) =
of_property_read_bool(node, itr->dt_name);
ret = 0;
break;
default:
dev_err(&pdev->dev, "%d is an unknown DT entry type\n",
itr->type);
ret = -EBADE;
}
dev_dbg(&pdev->dev, "DT entry ret:%d name:%s val:%d\n",
ret, itr->dt_name, *((int *)itr->ptr_data));
if (ret) {
*((int *)itr->ptr_data) = itr->default_val;
if (itr->status < DT_OPTIONAL) {
dev_err(&pdev->dev, "Missing '%s' DT entry\n",
itr->dt_name);
/* cont on err to dump all missing entries */
if (itr->status == DT_REQUIRED && !err)
err = ret;
}
}
}
return err;
}
static u32
qup_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
}
static const struct i2c_algorithm qup_i2c_algo = {
.master_xfer = qup_i2c_xfer,
.functionality = qup_i2c_func,
};
static int __devinit
qup_i2c_probe(struct platform_device *pdev)
{
struct qup_i2c_dev *dev;
struct resource *qup_mem, *gsbi_mem, *qup_io, *gsbi_io, *res;
struct resource *in_irq, *out_irq, *err_irq;
struct clk *clk, *pclk;
int ret = 0;
int i;
int dt_gpios[I2C_GPIOS_DT_CNT];
bool use_device_tree = pdev->dev.of_node;
struct msm_i2c_platform_data *pdata;
gsbi_mem = NULL;
dev_dbg(&pdev->dev, "qup_i2c_probe\n");
if (use_device_tree) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
ret = msm_i2c_rsrcs_dt_to_pdata_map(pdev, pdata, dt_gpios);
if (ret)
goto get_res_failed;
} else
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "platform data not initialized\n");
return -ENOSYS;
}
qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"qup_phys_addr");
if (!qup_mem) {
dev_err(&pdev->dev,
"platform_get_resource_byname(qup_phys_addr) failed\n");
ret = -ENODEV;
goto get_res_failed;
}
/*
* We only have 1 interrupt for new hardware targets and in_irq,
* out_irq will be NULL for those platforms
*/
in_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"qup_in_intr");
out_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"qup_out_intr");
err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"qup_err_intr");
if (!err_irq) {
dev_err(&pdev->dev, "no error irq resource?\n");
ret = -ENODEV;
goto get_res_failed;
}
qup_io = request_mem_region(qup_mem->start, resource_size(qup_mem),
pdev->name);
if (!qup_io) {
dev_err(&pdev->dev, "QUP region already claimed\n");
ret = -EBUSY;
goto get_res_failed;
}
if (!pdata->use_gsbi_shared_mode) {
gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"gsbi_qup_i2c_addr");
if (!gsbi_mem) {
dev_dbg(&pdev->dev, "Assume BLSP\n");
/*
* BLSP core does not need protocol programming so this
* resource is not expected
*/
goto blsp_core_init;
}
gsbi_io = request_mem_region(gsbi_mem->start,
resource_size(gsbi_mem),
pdev->name);
if (!gsbi_io) {
dev_err(&pdev->dev, "GSBI region already claimed\n");
ret = -EBUSY;
goto err_res_failed;
}
}
blsp_core_init:
clk = clk_get(&pdev->dev, "core_clk");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Could not get core_clk\n");
ret = PTR_ERR(clk);
goto err_clk_get_failed;
}
pclk = clk_get(&pdev->dev, "iface_clk");
if (IS_ERR(pclk)) {
dev_err(&pdev->dev, "Could not get iface_clk\n");
ret = PTR_ERR(pclk);
clk_put(clk);
goto err_clk_get_failed;
}
/* We support frequencies upto FAST Mode(400KHz) */
if (pdata->clk_freq <= 0 ||
pdata->clk_freq > 400000) {
dev_err(&pdev->dev, "clock frequency not supported\n");
ret = -EIO;
goto err_config_failed;
}
dev = kzalloc(sizeof(struct qup_i2c_dev), GFP_KERNEL);
if (!dev) {
ret = -ENOMEM;
goto err_alloc_dev_failed;
}
dev->dev = &pdev->dev;
if (in_irq)
dev->in_irq = in_irq->start;
if (out_irq)
dev->out_irq = out_irq->start;
dev->err_irq = err_irq->start;
if (in_irq && out_irq)
dev->num_irqs = 3;
else
dev->num_irqs = 1;
dev->clk = clk;
dev->pclk = pclk;
dev->base = ioremap(qup_mem->start, resource_size(qup_mem));
if (!dev->base) {
ret = -ENOMEM;
goto err_ioremap_failed;
}
/* Configure GSBI block to use I2C functionality */
if (gsbi_mem) {
dev->gsbi = ioremap(gsbi_mem->start, resource_size(gsbi_mem));
if (!dev->gsbi) {
ret = -ENOMEM;
goto err_gsbi_failed;
}
}
for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
if (use_device_tree && i < I2C_GPIOS_DT_CNT) {
dev->i2c_gpios[i] = dt_gpios[i];
} else {
res = platform_get_resource_byname(pdev, IORESOURCE_IO,
i2c_rsrcs[i]);
dev->i2c_gpios[i] = res ? res->start : -1;
}
}
platform_set_drvdata(pdev, dev);
dev->one_bit_t = (USEC_PER_SEC/pdata->clk_freq) + 1;
dev->pdata = pdata;
dev->clk_ctl = 0;
dev->pos = 0;
ret = i2c_qup_clk_path_init(pdev, dev);
if (ret) {
dev_err(&pdev->dev,
"Failed to init clock path-voting data structs. err:%d", ret);
/* disable i2c_qup_clk_path_xxx() functionality */
dev->pdata->master_id = 0;
}
if (dev->pdata->src_clk_rate <= 0) {
dev_info(&pdev->dev,
"No src_clk_rate specified in platfrom data\n");
dev_info(&pdev->dev, "Using default clock rate %dHz\n",
DEFAULT_CLK_RATE);
dev->pdata->src_clk_rate = DEFAULT_CLK_RATE;
}
ret = clk_set_rate(dev->clk, dev->pdata->src_clk_rate);
if (ret)
dev_info(&pdev->dev, "clk_set_rate(core_clk, %dHz):%d\n",
dev->pdata->src_clk_rate, ret);
clk_prepare_enable(dev->clk);
clk_prepare_enable(dev->pclk);
/*
* If bootloaders leave a pending interrupt on certain GSBI's,
* then we reset the core before registering for interrupts.
*/
writel_relaxed(1, dev->base + QUP_SW_RESET);
if (qup_i2c_poll_state(dev, 0, true) != 0)
goto err_reset_failed;
clk_disable_unprepare(dev->clk);
clk_disable_unprepare(dev->pclk);
/*
* We use num_irqs to also indicate if we got 3 interrupts or just 1.
* If we have just 1, we use err_irq as the general purpose irq
* and handle the changes in ISR accordingly
* Per Hardware guidelines, if we have 3 interrupts, they are always
* edge triggering, and if we have 1, it's always level-triggering
*/
if (dev->num_irqs == 3) {
ret = request_irq(dev->in_irq, qup_i2c_interrupt,
IRQF_TRIGGER_RISING, "qup_in_intr", dev);
if (ret) {
dev_err(&pdev->dev, "request_in_irq failed\n");
goto err_request_irq_failed;
}
/*
* We assume out_irq exists if in_irq does since platform
* configuration either has 3 interrupts assigned to QUP or 1
*/
ret = request_irq(dev->out_irq, qup_i2c_interrupt,
IRQF_TRIGGER_RISING, "qup_out_intr", dev);
if (ret) {
dev_err(&pdev->dev, "request_out_irq failed\n");
free_irq(dev->in_irq, dev);
goto err_request_irq_failed;
}
ret = request_irq(dev->err_irq, qup_i2c_interrupt,
IRQF_TRIGGER_RISING, "qup_err_intr", dev);
if (ret) {
dev_err(&pdev->dev, "request_err_irq failed\n");
free_irq(dev->out_irq, dev);
free_irq(dev->in_irq, dev);
goto err_request_irq_failed;
}
} else {
ret = request_irq(dev->err_irq, qup_i2c_interrupt,
IRQF_TRIGGER_HIGH, "qup_err_intr", dev);
if (ret) {
dev_err(&pdev->dev, "request_err_irq failed\n");
goto err_request_irq_failed;
}
}
disable_irq(dev->err_irq);
if (dev->num_irqs == 3) {
disable_irq(dev->in_irq);
disable_irq(dev->out_irq);
}
i2c_set_adapdata(&dev->adapter, dev);
dev->adapter.algo = &qup_i2c_algo;
strlcpy(dev->adapter.name,
"QUP I2C adapter",
sizeof(dev->adapter.name));
dev->adapter.nr = pdev->id;
dev->adapter.dev.parent = &pdev->dev;
if (pdata->msm_i2c_config_gpio)
pdata->msm_i2c_config_gpio(dev->adapter.nr, 1);
mutex_init(&dev->mlock);
dev->pwr_state = MSM_I2C_PM_SUSPENDED;
atomic_set(&dev->xfer_progress, 0);
/* If the same AHB clock is used on Modem side
* switch it on here itself and don't switch it
* on and off during suspend and resume.
*/
if (dev->pdata->keep_ahb_clk_on)
clk_prepare_enable(dev->pclk);
ret = i2c_add_numbered_adapter(&dev->adapter);
if (ret) {
dev_err(&pdev->dev, "i2c_add_adapter failed\n");
if (dev->num_irqs == 3) {
free_irq(dev->out_irq, dev);
free_irq(dev->in_irq, dev);
}
free_irq(dev->err_irq, dev);
} else {
if (dev->dev->of_node) {
dev->adapter.dev.of_node = pdev->dev.of_node;
of_i2c_register_devices(&dev->adapter);
}
pm_runtime_set_autosuspend_delay(&pdev->dev, MSEC_PER_SEC);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
}
err_request_irq_failed:
if (dev->gsbi)
iounmap(dev->gsbi);
err_reset_failed:
clk_disable_unprepare(dev->clk);
clk_disable_unprepare(dev->pclk);
i2c_qup_clk_path_teardown(dev);
err_gsbi_failed:
iounmap(dev->base);
err_ioremap_failed:
kfree(dev);
err_alloc_dev_failed:
err_config_failed:
clk_put(clk);
clk_put(pclk);
err_clk_get_failed:
if (gsbi_mem)
release_mem_region(gsbi_mem->start, resource_size(gsbi_mem));
err_res_failed:
release_mem_region(qup_mem->start, resource_size(qup_mem));
get_res_failed:
if (pdev->dev.of_node)
kfree(pdata);
return ret;
}
static void qup_i2c_mem_release(struct platform_device *pdev, const char *name)
{
struct resource *res =
platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
if (res)
release_mem_region(res->start, resource_size(res));
else
dev_dbg(&pdev->dev,
"platform_get_resource_byname(%s) failed\n", name);
}
static int __devexit
qup_i2c_remove(struct platform_device *pdev)
{
struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
/* Grab mutex to ensure ongoing transaction is over */
mutex_lock(&dev->mlock);
dev->pwr_state = MSM_I2C_SYS_SUSPENDING;
mutex_unlock(&dev->mlock);
i2c_qup_pm_suspend(dev);
dev->pwr_state = MSM_I2C_SYS_SUSPENDED;
mutex_destroy(&dev->mlock);
platform_set_drvdata(pdev, NULL);
if (dev->num_irqs == 3) {
free_irq(dev->out_irq, dev);
free_irq(dev->in_irq, dev);
}
free_irq(dev->err_irq, dev);
i2c_del_adapter(&dev->adapter);
if (!dev->pdata->keep_ahb_clk_on) {
clk_put(dev->pclk);
}
clk_put(dev->clk);
if (dev->pdata->active_only)
i2c_qup_clk_path_unvote(dev);
i2c_qup_clk_path_teardown(dev);
if (dev->gsbi)
iounmap(dev->gsbi);
iounmap(dev->base);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
if (!(dev->pdata->use_gsbi_shared_mode))
qup_i2c_mem_release(pdev, "gsbi_qup_i2c_addr");
qup_i2c_mem_release(pdev, "qup_phys_addr");
if (dev->dev->of_node)
kfree(dev->pdata);
kfree(dev);
return 0;
}
#ifdef CONFIG_PM
static int i2c_qup_pm_suspend_runtime(struct device *device)
{
struct platform_device *pdev = to_platform_device(device);
struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
dev_dbg(device, "pm_runtime: suspending...\n");
i2c_qup_pm_suspend(dev);
return 0;
}
static int i2c_qup_pm_resume_runtime(struct device *device)
{
struct platform_device *pdev = to_platform_device(device);
struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
dev_dbg(device, "pm_runtime: resuming...\n");
i2c_qup_pm_resume(dev);
return 0;
}
static int i2c_qup_pm_suspend_sys(struct device *device)
{
struct platform_device *pdev = to_platform_device(device);
struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
/* Acquire mutex to ensure current transaction is over */
mutex_lock(&dev->mlock);
dev->pwr_state = MSM_I2C_SYS_SUSPENDING;
mutex_unlock(&dev->mlock);
if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) {
dev_dbg(device, "system suspend\n");
i2c_qup_pm_suspend(dev);
/*
* set the device's runtime PM status to 'suspended'
*/
pm_runtime_disable(device);
pm_runtime_set_suspended(device);
pm_runtime_enable(device);
}
dev->pwr_state = MSM_I2C_SYS_SUSPENDED;
return 0;
}
static int i2c_qup_pm_resume_sys(struct device *device)
{
struct platform_device *pdev = to_platform_device(device);
struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
/*
* Rely on runtime-PM to call resume in case it is enabled
* Even if it's not enabled, rely on 1st client transaction to do
* clock ON and gpio configuration
*/
dev_dbg(device, "system resume\n");
dev->pwr_state = MSM_I2C_PM_SUSPENDED;
return 0;
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops i2c_qup_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(
i2c_qup_pm_suspend_sys,
i2c_qup_pm_resume_sys
)
SET_RUNTIME_PM_OPS(
i2c_qup_pm_suspend_runtime,
i2c_qup_pm_resume_runtime,
NULL
)
};
static struct of_device_id i2c_qup_dt_match[] = {
{
.compatible = "qcom,i2c-qup",
},
{}
};
static struct platform_driver qup_i2c_driver = {
.probe = qup_i2c_probe,
.remove = __devexit_p(qup_i2c_remove),
.driver = {
.name = "qup_i2c",
.owner = THIS_MODULE,
.pm = &i2c_qup_dev_pm_ops,
.of_match_table = i2c_qup_dt_match,
},
};
/* QUP may be needed to bring up other drivers */
int __init qup_i2c_init_driver(void)
{
static bool initialized;
if (initialized)
return 0;
else
initialized = true;
return platform_driver_register(&qup_i2c_driver);
}
EXPORT_SYMBOL(qup_i2c_init_driver);
arch_initcall(qup_i2c_init_driver);
static void __exit qup_i2c_exit_driver(void)
{
platform_driver_unregister(&qup_i2c_driver);
}
module_exit(qup_i2c_exit_driver);
使用中注意设备树中配成相应的
static struct of_device_id i2c_qup_dt_match[] = {
{
.compatible = "qcom,i2c-qup",
},
{}
};
第一:
arch_initcall(qup_i2c_init_driver);
第二:
platform_driver_register(&qup_i2c_driver);
static struct platform_driver qup_i2c_driver = {
.probe = qup_i2c_probe,
.remove = __devexit_p(qup_i2c_remove),
.driver = {
.name = "qup_i2c",
.owner = THIS_MODULE,
.pm = &i2c_qup_dev_pm_ops,
.of_match_table = i2c_qup_dt_match,
},
};
在probe中,先提取platform_device 的设备信息填充i2c平台数据(struct msm_i2c_platform_data) *pdata;
struct device_node *node = pdev->dev.of_node;
compatible = “qcom,i2c-qup”; 用于与驱动match的
reg-names = “qup_phys_addr”;
reg = <0xf9923000 0x1000>;
qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
“qup_phys_addr”);
这个用于一块的IORESOURCE_MEM (resource)的数据结构(struct resource *qup_mem)
interrupt-names = “qup_err_intr”;
interrupts = <0 95 0>;
err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
“qup_err_intr”);
用于读取IORESOURCE_IRQ资源,
ret = of_get_named_gpio(node, itr->dt_name, 0);
static inline int of_get_named_gpio(struct device_node *np,const char *propname, int index)
{
return of_get_named_gpio_flags(np, propname, index, NULL);*返回的是GPIO的有效值,使用Linux通用GPIO的API,或者是一个错误
*值在错误条件。如果“标记”不为空,则函数也填充
对于GPIO标志*。
}
ret = of_property_read_u32(node, itr->dt_name,(u32 *) itr->ptr_data);
static inline int of_property_read_u32(const struct device_node *np,const char *propname, u32 *out_value)
{
return of_property_read_u32_array(np, propname, out_value, 1);*在设备节点中搜索一个属性,并从中读取32位值到out_value(S)
*。成功执行将返回0
}
((bool ) itr->ptr_data) =of_property_read_bool(node, itr->dt_name);等
还有将
qup_io = request_mem_region(qup_mem->start,resource_size(qup_mem),pdev->name);
1
request_mem_region 是申请 I/O 内存用的 . 申请了之后 , 还需要使用 ioremap 或者 ioremap_nocache 函数来映射 .对于 request_region, 三个参数 start,n,name 表示你想使用从 start 开始的 size 为 n 的 I/O port 资源 ,name 自然就是你的名字。
这样i2c的一个接口设备驱动就完成了,基础就有了。
重要的平台驱动注册。platform_driver_register
platform_driver_register()的注册过程:
1 platform_driver_register(&qup_i2c_driver)
2 driver_register(&drv->driver)
3 bus_add_driver(drv)
4 driver_attach(drv)
5 bus_for_each_dev(drv->bus, NULL, drv,__driver_attach)
6 __driver_attach(struct device * dev, void * data)
7 driver_probe_device(drv, dev)
8 really_probe(dev, drv)
在really_probe()中:为设备指派管理该设备的驱动:dev->driver = drv, 调用probe()函数初始化设备:drv->probe(dev)
这是完成平台驱动的注册和设备的probe。
sys/bus/platfrom的模型
//platform设备声明
struct device platform_bus = {
.bus_id = "platform",
};
EXPORT_SYMBOL_GPL(platform_bus);
//platform总线设备声明
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume,
};
EXPORT_SYMBOL_GPL(platform_bus_type);
这里定义了两个数据结构分别是
/sys/devices/platfrom/
/sys/bus/platfrom/
int __init platform_bus_init(void)
{
int error;
error = device_register(&platform_bus);//注册了"platform"的设备
if (error)
return error;
error = bus_register(&platform_bus_type);//注册了叫"platform"的总线
if (error)
device_unregister(&platform_bus);
return error;
}
有了总线就可以挂设备了。
//常用的platform_device_register,内部调用了platform_device_add,将设备挂在了platform总线上
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*/
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_register);
//这里在platform总线上挂设备
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;//父设备设置为platform_bus
pdev->dev.bus = &platform_bus_type;//设置挂在platform总线上
if (pdev->id != -1)
snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
pdev->id);
else
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = pdev->dev.bus_id;
p = r->parent;
if (!p) {
if (r->flags & IORESOURCE_MEM)
p = &iomem_resource;
else if (r->flags & IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d\n",
pdev->dev.bus_id, i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
pdev->dev.bus_id, pdev->dev.parent->bus_id);
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
while (--i >= 0)
if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
release_resource(&pdev->resource[i]);
return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);
blsp_core_init(blsp的初始化)
if (!pdata->use_gsbi_shared_mode)
判断是否使用gsbi
gsbi是物理上实际存在的部件,它可以模拟成i2c、gpio、spi、uart、sdio等,此i2c即是gsbi模拟的。这里顺便提下i2c adapter和i2c总线,两者都是物理上必须存在的,哪怕是其他部件模拟的,例如GSBI模拟I2C,那么GSBI这个部件是实际存在的。GSBI的走线会与所有i2c设备互连,GSBI的接口即是i2c adapter,与其他i2c设备互连的走线即是i2c总线。如果GSBI模拟成uart,那么与uart的rx/tx pin连接的走线即是uart串口线。这是高通平台的设计,换作其他平台,也一定会有一个i2c adapter或者说可以模拟成i2c adapter的物理部件存在。猜测i2c adapter位于片上系统中,类似I/O接口的东西,没有固件,驱动它的代码即是各平台相关的i2c adapter driver。GSBI的地址以及模拟成的qup i2c/uart的地址都是编址好的,在arch/arm/mach-msm/devices-msm8x60.c最开头可以看到这些物理地址。我们知道ARM是统一编址的,在代码中要用ioremap把它映射到虚拟内存,这样就可以访问了。
gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"gsbi_qup_i2c_addr");
if (!gsbi_mem) {
dev_dbg(&pdev->dev, "Assume BLSP\n");
/*
* BLSP core does not need protocol programming so this
* resource is not expected
*/
goto blsp_core_init;
}
也就是不是Gsbi时goto blsp_core_init;
获取clk;
获取pclk;
实例化一个dev(struct qup_i2c_dev)
dev->dev = &pdev->dev;
dev->err_irq = err_irq->start;
dev->clk = clk;
dev->pclk = pclk;
dev->base = ioremap(qup_mem->start, resource_size(qup_mem));//映射到虚拟地址
dev->i2c_gpios[i] = dt_gpios[i];//获取sda scl;
platform_set_drvdata(pdev, dev);
pdev是我们在probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? 这就需要把它保存起来。内核提供了这个方法,使用函数platform_set_drvdata()可以将pdev保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了。
pdata = pdev->dev.platform_data;dev->pdata = pdata;
ret = i2c_qup_clk_path_init(pdev, dev);
ret = request_irq(dev->err_irq, qup_i2c_interrupt,IRQF_TRIGGER_HIGH, “qup_err_intr”, dev);
disable_irq(dev->err_irq);
i2c_set_adapdata(&dev->adapter, dev);
dev->adapter.algo = &qup_i2c_algo;//等等
ret = i2c_add_numbered_adapter(&dev->adapter); //i2c-core.c添加适配器是core层做的事。主要i2c_register_adapter(adap);
关于i2c_register_adapter(adap)详解一下;
适配器的存在就是一个设备的存在,按照希望的过程是在/sys/bus/下构建一个i2c的驱动模型框架。设备模型是希望一个功能可以通过某个设备和某个设备驱动probe成功,让驱动完成与应用层的交互。devices这个数据结构就是一个抽象(可以说是个类的概念)
它就有着device_register(struct device *),它与sysfs的体现就是构建了devices的管理路径.
INIT_LIST_HEAD(&adap->userspace_clients);// 初始化一个clients的双向链表;
dev_set_name(&adap->dev, “i2c-%d”, adap->nr);//设置一个具体的适配器设备文件名。
adap->dev.bus = &i2c_bus_type;
struct bus_type i2c_bus_type = {
.name = "i2c",// /sys/bus/i2c 设备模型的框
.match = i2c_device_match,//这个设备match函数
.probe = i2c_device_probe,//这个设备的probe函数
.remove = i2c_device_remove,//驱动与设备的移除
.shutdown = i2c_device_shutdown,//设备关闭
.pm = &i2c_device_pm_ops,//电源管理
};
1
2
3
4
5
6
7
8
adap->dev.type = &i2c_adapter_type;//这个设备(i2c适配器)的sysfs的接口应用
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);
static struct attribute *i2c_adapter_attrs[] = {
&dev_attr_name.attr,
&dev_attr_new_device.attr,
&dev_attr_delete_device.attr,
NULL
};
static struct attribute_group i2c_adapter_attr_group = {
.attrs = i2c_adapter_attrs,
};
static const struct attribute_group *i2c_adapter_attr_groups[] = {
&i2c_adapter_attr_group,
NULL
};
struct device_type i2c_adapter_type = {
.groups = i2c_adapter_attr_groups,
.release = i2c_adapter_dev_release,
};
res = device_register(&adap->dev); //设备的注册
(难点)
/* 相应的代码位于i2c-core.c:create pre-declared device nodes */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);.......
struct i2c_devinfo {
struct list_head list;
int busnum;
struct i2c_board_info board_info;
};
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
void *platform_data;
struct dev_archdata *archdata;
struct device_node *of_node;
int irq;
};
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
在i2c_scan_static_board_info()函数中遍历I2C设备链表__i2c_board_list,设备的总线号和adapter(nr)的总线号相等,
回顾下在设备树中获取总线号的方法,当然也是配置的方法:
{“cell-index”, &pdev->id, DT_REQUIRED, DT_U32, -1},
adapter->nr = pdev->id;
dev_set_name(&adap->dev, “i2c-%d”, adap->nr);
至于I2C设备链表的创建和填充是在
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
struct device_node *child;
int rc = 0;
root = root ? of_node_get(root) : of_find_node_by_path("/");
if (!root)
return -EINVAL;
for_each_child_of_node(root, child) { //遍历设备树
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc)
break;
}
of_node_put(root);
return rc;
}
*创建一个platform_device为device_node
*递归为所有子节点创建设备。
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
const struct of_dev_auxdata *auxdata;
struct device_node *child;
struct platform_device *dev;
const char *bus_id = NULL;
void *platform_data = NULL;
int rc = 0;
/* Make sure it has a compatible property */
if (strict && (!of_get_property(bus, "compatible", NULL))) {
pr_debug("%s() - skipping %s, no compatible prop\n",
__func__, bus->full_name);
return 0;
}
auxdata = of_dev_lookup(lookup, bus);
if (auxdata) {
bus_id = auxdata->name;
platform_data = auxdata->platform_data;
}
if (of_device_is_compatible(bus, "arm,primecell")) {
of_amba_device_create(bus, bus_id, platform_data, parent);
return 0;
}
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
for_each_child_of_node(bus, child) {
pr_debug(" create child: %s\n", child->full_name);
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
if (rc) {
of_node_put(child);
break;
}
}
return rc;
}
在适配器probe时如果是设备树的方式注册。
if (dev->dev->of_node) {
dev->adapter.dev.of_node = pdev->dev.of_node;
of_i2c_register_devices(&dev->adapter);
}
void of_i2c_register_devices(struct i2c_adapter *adap)
{
void *result;
struct device_node *node;
/* Only register child devices if the adapter has a node pointer set */
if (!adap->dev.of_node)
return;
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
for_each_child_of_node(adap->dev.of_node, node) {
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr;
int len;
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
node->full_name);
continue;
}
addr = of_get_property(node, "reg", &len);
if (!addr || (len < sizeof(int))) {
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
node->full_name);
continue;
}
info.addr = be32_to_cpup(addr);
if (info.addr > (1 << 10) - 1) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
info.addr, node->full_name);
continue;
}
info.irq = irq_of_parse_and_map(node, 0);
info.of_node = of_node_get(node);
info.archdata = &dev_ad;
request_module("%s%s", I2C_MODULE_PREFIX, info.type);
result = i2c_new_device(adap, &info);
if (result == NULL) {
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
node->full_name);
of_node_put(node);
irq_dispose_mapping(info.irq);
continue;
}
}
}
如果使用board_info 的方式则是遍历devices链表,当设备的busnum等于这个i2c的总线号就确认这个设备是挂在这i2c的总线上的。
最后都是使用函数i2c_new_device()创建该设备。
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;
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)
goto out_err;
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;
/* For 10-bit clients, add an arbitrary offset to avoid collisions */
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr | ((client->flags & I2C_CLIENT_TEN)
? 0xa000 : 0));
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;
}
通过struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);
传入的适配器和i2c_board_info构建成返回一个i2c_client,(如果返回NULL 一般就是地址的有误或冲突)
client->dev.parent = &client->adapter->dev;//将一个设备的父容器dev设置为其适配器的设备容器
client->dev.bus = &i2c_bus_type;//设备总线描述(在bus/i2c/{下的设备}devices/)
client->dev.type = &i2c_client_type;//设备类型描述(att『属性』,uevent,release等)
client->dev.of_node = info->of_node;//设备树的节点,类似pdate,
dev_set_name(&client->dev, “%d-%04x”, i2c_adapter_id(adap),
client->addr | ((client->flags & I2C_CLIENT_TEN)? 0xa000 : 0));//设备的命名 如:
/sys/bus/i2c/devices/1-0038/
通过设备容器的嵌套:1-0038 -> ../../../devices/f9923000.i2c/i2c-1/1-0038
也就是实际上sys/bus/i2c/devices/* 是/sys/devices/的链接文件。
status = device_register(&client->dev);//完成上面的设置。
这就完成了一个client。而这个client构建的时候也是具有了适配器,算法,等。
注意:
*bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
新的适配器加入内核时调用函数 bus_for_each_drv时调用的函数。
完成当一个新的适配器加入内核时,会在总线类型为i2c_bus_type的驱动中找与之匹配的,
并将驱动支持的设备插入新加入的这个适配器。
函数bus_for_each_dev() 和 bus_for_each_drv()分别用于遍历bus上devices和drivers链表中的所有元素。*
这部分在已经废弃了,不在建议使用
/ *通知BUS的机制,一个新的公共bus已经出现,它即将
*删除。你应该避免使用这个,不久的将来它将被删除
* /
int (attach_adapter)(struct i2c_adapter ) __deprecated;
int (detach_adapter)(struct i2c_adapter ) __deprecated;
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared or is about to be
* removed. You should avoid using this, it will be removed in a
* near future.
*/
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
int (*detach_adapter)(struct i2c_adapter *) __deprecated;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
*/
void (*alert)(struct i2c_client *, unsigned int data);
/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
};
struct i2c_device_id {
char name[I2C_NAME_SIZE]; //它由于对比具体的client的 name于 设备的驱动probe
kernel_ulong_t driver_data /* Data private to the driver */
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
一、关于client与设备驱动的匹配
到内核,这些设备client的name在该驱动的id_table中。
老的内核还是通过函数driver->detect便是填充结构体i2c_board_info,用i2c_board_info去初始化
client,驱动id_table中的name就是i2c_board_info->type带入client的。
该驱动与设备就是根据这个name相匹配的,匹配后调用函数probe执行一些初始化或是设备探测。
当然该驱动也是可以不携带地址信息的,而是注册后与内核中已存在的client匹配执行probe()。
“legacy”driver是不携带地址信息的,它的主要任务是调用函数driver->detach_adapter为新加入
的适配器,或是与之匹配的适配器创建一个可依附的设备节点,或者是做其他初始化工作。
新的内核就通过设备树的方式,将一个适配器和它附属的设备顺序的完成clinet的创建。
驱动实际只需要要匹配设备name就可以probe,和使用设备依赖的适配器的算法和设备数据。
编写一个设备驱动就简单的很多。
如:
module_init(goodix_ts_init); //调用等级为7.
static struct i2c_driver goodix_ts_driver = {
.probe = goodix_ts_probe,
.remove = goodix_ts_remove,
.id_table = goodix_ts_id, //I2C设备支持的驱动程序列表 /sys/bus/i2c/drivers/XXXX
.driver = {
.name = GTP_I2C_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = goodix_match_table,
#endif
#if !defined(CONFIG_FB) && defined(CONFIG_PM)
.pm = >p_pm_ops,
#endif
},
};
1
定义一个i2c_driver的数据结构;
static const struct of_device_id goodix_match_table[] = {
{.compatible = "goodix,gt915l",},
{ },
};
1
2
3
4
of_device_id为了实现于client的probe。(client->name)///sys/bus/i2c/devices/1-005d/name
驱动可以驱动多个设备,但是设备被一个驱动服务。
ret = i2c_add_driver(&goodix_ts_driver);//调用一个add函数,它具体完成i2c设备的内核注册。
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)//他是core层代码。
1
2
driver->driver.owner = owner; //THIS_MODULE
driver->driver.bus = &i2c_bus_type; //sys/bus/i2c/driver/XXXX
res = driver_register(&driver->driver); //类似device_register();
INIT_LIST_HEAD(&driver->clients); //初始化client的链表头。
i2c_for_each_dev(driver, __process_new_driver); //find已经存在的适配器 //新的内核已经废弃。
static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
client :传递的适配器。
id :传递驱动(name ,data)。
probe成功后只需要保存这个client的数据结构,就可以操作具体的设备了。
---------------------
作者:lsn946803746
来源:优快云
原文:https://blog.youkuaiyun.com/lsn946803746/article/details/52515225
版权声明:本文为博主原创文章,转载请附上博文链接!