catalogue
i2c module block diagram ug-1085(ch22)
2. Device tree of controller and slave devices
3. Check i2c controller driver registration process: standard platform driver framework
Interface for reading temperature from the device:
The essence of reading and writing: finally, it is to read and write through adapter - > algo in the client
IIC controller based on zynqmp
i2c module block diagram ug-1085(ch22)
(a) There are two I2C buses i2c0 and i2c1 interrupt IDS 49 and 50; CPU accesses I2C controller through APB bus, and I2C controller links external devices through scl and sda;
(b) Interrupt id: ug-1085(13) the interrupt controller is GIC
(c) Register and data flow block diagram
Cpu accesses the peripherals mounted on i2c bus through APB bus, and controls the communication between i2c controller and peripherals (slave devices) through four registers (control register, sending data register, receiving data register and status register);
Addresses corresponding to these registers (ug1087)
These registers are generally used during the transmission initialization of the controller, which will be used in the later controller driver;
2. Device tree of controller and slave devices
pd_i2c1: pd-i2c1 { #power-domain-cells = <0x0>; pd-id = <0x26>; }; i2c1: i2c@ff030000 { compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10"; /* Matching controller drive */ status = "okay"; /* Indicates whether this node is effective */ clock-frequency = <100000>; /* scl Clock*/ interrupt-parent = <&gic>; /*The parent interrupt is the gic interrupt controller*/ interrupts = <0 18 4>; /* Interrupt No. 18 high level*/ reg = <0x0 0xff030000 0x0 0x1000>; /* i2c Address base */ #address-cells = <1>; #size-cells = <0>; power-domains = <&pd_i2c1>; tmp401: tmp401@4c { /* u23 */ compatible = "ti,tmp401"; reg = <0x4c>; }; };
Search the source code according to the device tree: grep "cdns,i2c-r1p" - r | grep -v "dts"
3. Check i2c controller driver registration process: standard platform driver framework
Execute CDNs after matching_ i2c_ Probe function;
Several things to do in probe:
- Obtain the device tree resources and use these resources to initialize i2c registers (control, send, receive, status, interrupt);
- Build an i2c_adapter and initialize the method that must implement the member algo
- id->adap.algo = &cdns_i2c_algo;
- master_xfer and functionality
- Initialize the clock registration interrupt (because the interrupt is used to send and receive data);
- devm_request_irq(&pdev->dev, id->irq, cdns_i2c_isr, 0,DRIVER_NAME, id);
- Initialize and register an adapter with the bus; i2c_ add_ adapter(&id->adap);
Analyze probe function:
static int cdns_i2c_probe(struct platform_device *pdev) { struct resource *r_mem; struct cdns_i2c *id; int ret; const struct of_device_id *match; /* Give struct CDNs_ The I2C application space contains adapters and other resources */ id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL); id->dev = &pdev->dev; /* Save platform device */ platform_set_drvdata(pdev, id); /* Save struct cdns_i2c to drive data */ match = of_match_node(cdns_i2c_of_match, pdev->dev.of_node); if (match && match->data) { const struct cdns_platform_data *data = match->data; id->quirks = data->quirks; // 0 } id->pinctrl = devm_pinctrl_get(&pdev->dev); if (!IS_ERR(id->pinctrl)) { ret = cdns_i2c_init_recovery_info(id, pdev); if (ret) return ret; } /* Get base address from device tree */ r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); id->membase = devm_ioremap_resource(&pdev->dev, r_mem); if (IS_ERR(id->membase)) return PTR_ERR(id->membase); /* Get interrupt number */ id->irq = platform_get_irq(pdev, 0); /* Initialize adapter */ id->adap.owner = THIS_MODULE; id->adap.dev.of_node = pdev->dev.of_node; id->adap.algo = &cdns_i2c_algo; id->adap.timeout = CDNS_I2C_TIMEOUT; id->adap.retries = 3; /* Default retry value. */ id->adap.algo_data = id; id->adap.dev.parent = &pdev->dev; init_completion(&id->xfer_done); snprintf(id->adap.name, sizeof(id->adap.name), "Cadence I2C at %08lx", (unsigned long)r_mem->start); id->clk = devm_clk_get(&pdev->dev, NULL); ret = clk_prepare_enable(id->clk); pm_runtime_set_autosuspend_delay(id->dev, CNDS_I2C_PM_TIMEOUT); pm_runtime_use_autosuspend(id->dev); pm_runtime_set_active(id->dev); pm_runtime_enable(id->dev); id->clk_rate_change_nb.notifier_call = cdns_i2c_clk_notifier_cb; ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &id->i2c_clk); if (ret || (id->i2c_clk > CDNS_I2C_SPEED_MAX)) id->i2c_clk = CDNS_I2C_SPEED_DEFAULT; /* It refers to the control register status enabling ack 7-bit transmission master mode */ id->ctrl_reg = CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS; /* set clock */ ret = cdns_i2c_setclk(id->input_clk, id); /* Registration interruption */ ret = devm_request_irq(&pdev->dev, id->irq, cdns_i2c_isr, 0,DRIVER_NAME, id); /* Set timeout ID - > Ctrl_ Reg write register */ cdns_i2c_init(id); /* Register adapter */ ret = i2c_add_adapter(&id->adap); if (ret < 0) goto err_clk_dis; dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n", id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq); return 0; }
Mainly look at cdns_i2c_algo and interrupt handling function cdns_i2c_isr:
static const struct i2c_algorithm cdns_i2c_algo = {
. master_xfer = cdns_i2c_master_xfer, //i2c-core-base.c medium__ i2c_transfer will call
. functionality = cdns_i2c_func, / / is a master_ Types of iic drivers that xfer interface can support
};
master_xfer is invoked in the read write interface and is wrapped in the core layer. i2c_ The transfer is as follows:
CDNs in zynqmp_ i2c_ master_ Implementation of xfer:
In this function, you can see some operations on registers, control registers, status registers, etc
static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num){ int ret, count; u32 reg; struct cdns_i2c *id = adap->algo_data; bool hold_quirk; ret = pm_runtime_get_sync(id->dev); /* CDNS_I2C_SR_OFFSET = 0x04; Read 0x4 status register CDNS_I2C_SR_BA: bit 8 bus is in transmission */ if (msgs->len) if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA) { //If the bus is in transmission, it will be returned directly ret = -EAGAIN; goto out; } hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT); //Repeat flag /* Set the flag to one when multiple messages are to be * processed with a repeated start. */ if (num > 1) { //Send multiple message branches for (count = 0; (count < num - 1 && hold_quirk); count++) { if (msgs[count].flags & I2C_M_RD) { dev_warn(adap->dev.parent, "Can't do repeated start after a receive message\n"); ret = -EOPNOTSUPP; goto out; } } id->bus_hold_flag = 1; reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); reg |= CDNS_I2C_CR_HOLD; cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET); // Control register set bit 4 } else { //Send single id->bus_hold_flag = 0; } /* Process the msg one by one */ for (count = 0; count < num; count++, msgs++) { if (count == (num - 1)) //Release the flag bit when the last transmission; Release sclk id->bus_hold_flag = 0; ret = cdns_i2c_process_msg(id, msgs, adap); //send message /* Report the other error interrupts to application */ if (id->err_status) { cdns_i2c_master_reset(adap); //If an error occurs during message sending, reset it. See how to reset later if (id->err_status & CDNS_I2C_IXR_NACK) { ret = -ENXIO; goto out; } ret = -EIO; goto out; } } ret = num; out: pm_runtime_mark_last_busy(id->dev); pm_runtime_put_autosuspend(id->dev); return ret; }
Cdns_ is called in the loop. i2c_ process_ Msg to send data: in this function, you can see that the reading status is mainly to distinguish between reading and writing and address bit width
static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg, struct i2c_adapter *adap) { unsigned long time_left; u32 reg; id->p_msg = msg; //Save the message to be sent to struct cdns_i2c structure id->err_status = 0; reinit_completion(&id->xfer_done); //Set ID - > xfer_ Done set to 0 /* Check for the TEN Bit mode on each msg */ reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); //Read control register 0x00 if (msg->flags & I2C_M_TEN) { //10 bit address if (reg & CDNS_I2C_CR_NEA) cdns_i2c_writereg(reg & ~CDNS_I2C_CR_NEA, CDNS_I2C_CR_OFFSET); } else { //7-bit address if (!(reg & CDNS_I2C_CR_NEA)) cdns_i2c_writereg(reg | CDNS_I2C_CR_NEA, CDNS_I2C_CR_OFFSET); } /* Check for zero length - Slave monitor mode */ if (msg->len == 0) cdns_i2c_slvmon(id); /* Check for the R/W flag on each msg */ else if (msg->flags & I2C_M_RD) //The host reads I2C from the slave device_ M_ RD= 1; cdns_i2c_mrecv(id); //receive data else cdns_i2c_msend(id); //send data /* Wait for the signal of completion */ time_left = wait_for_completion_timeout(&id->xfer_done, adap->timeout); //Wait for ID - > xfer_ Done set if (time_left == 0) { i2c_recover_bus(adap); cdns_i2c_master_reset(adap); dev_err(id->adap.dev.parent, "timeout waiting on completion\n"); return -ETIMEDOUT; } cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET); /* If it is bus arbitration error, try again */ if (id->err_status & CDNS_I2C_IXR_ARB_LOST) return -EAGAIN; return 0; }
cdns_i2c_mrecv: all the way down, I finally got to the place where I received the data (made a deletion)
Enable interrupt cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
static void cdns_i2c_mrecv(struct cdns_i2c *id) { unsigned int ctrl_reg; unsigned int isr_status; id->p_recv_buf = id->p_msg->buf; //preservation id->recv_count = id->p_msg->len; /* Put the controller in master receive mode and clear the FIFO */ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); ctrl_reg |= CDNS_I2C_CR_RW | CDNS_I2C_CR_CLR_FIFO; if (id->p_msg->flags & I2C_M_RECV_LEN) id->recv_count = I2C_SMBUS_BLOCK_MAX + 1; id->curr_recv_count = id->recv_count; inspect iic Length ratio of received data in fifo The depth of is large and needs to be transmitted many times; if (id->recv_count > CDNS_I2C_FIFO_DEPTH) ctrl_reg |= CDNS_I2C_CR_HOLD; cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET); //Clear interrupt in interrupt status register: 0x10 isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET); cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET); if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) { //Transmission size judgment 0x14 register cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE, CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE; } else { cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET); } //Interrupt enable bit [0 1 2 5 6 7 9] set to 0x24 interrupt enable register cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET); //Write from device address to 0x08 IIC address register cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK, CDNS_I2C_ADDR_OFFSET); /* Clear the bus hold flag if bytes to receive is less than FIFO size */ if (!id->bus_hold_flag && ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) && (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) cdns_i2c_clear_bus_hold(id); }
After reading this function, CDNs is found_ i2c_ How to read data without operation on data register (0x0C) in mrecv?? cdns_ i2c_ process_ In MSG, it enables interrupt and waits for ID - > xfer_ Done set; Search and you will know that it is the data transmitted in the interrupt; The interrupt handling function has been registered in the probe and has not been analyzed;
Analyze the interrupt processing function: the interrupt function finally sets ID - > xfer_ Done set; cdns_ i2c_ process_ Wait in MSG to judge whether this flag is set
cdns_i2c_isr Interrupt handling function: static irqreturn_t cdns_i2c_isr(int irq, void *ptr){ cdns_i2c_master_isr(ptr); return IRQ_HANDLED; } static irqreturn_t cdns_i2c_master_isr(void *ptr) //The final function of i2c control register operation { unsigned int isr_status, avail_bytes, updatetx; unsigned int bytes_to_send; bool hold_quirk; struct cdns_i2c *id = ptr; /* Signal completion only after everything is updated */ int done_flag = 0; irqreturn_t status = IRQ_NONE; /* Read the interrupt status and clear the interrupt status */ isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET); cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET); /* Detecting the interrupt state nack indicates that the slave responds or the host is arbitrating and returns directly*/ if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) { done_flag = 1; status = IRQ_HANDLED; } updatetx = 0; if (id->recv_count > id->curr_recv_count) updatetx = 1; hold_quirk = (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx; /* When receiving, handle data interrupt and completion interrupt */ if (id->p_recv_buf && ((isr_status & CDNS_I2C_IXR_COMP) || (isr_status & CDNS_I2C_IXR_DATA))) { /* To read data, first read the status register as the effective fifo depth of the read data*/ while(cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_RXDV) { if((id->recv_count<CDNS_I2C_FIFO_DEPTH)&&!id->bus_hold_flag) cdns_i2c_clear_bus_hold(id); /* The read data register is filled to P_ recv_ In buf */ *(id->p_recv_buf)++ = cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET); id->recv_count--; id->curr_recv_count--; if (cdns_is_holdquirk(id, hold_quirk)) break; } /*Large data transmission*/ if (cdns_is_holdquirk(id, hold_quirk)) { /* wait while fifo is full */ while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) != (id->curr_recv_count - CDNS_I2C_FIFO_DEPTH)); if(((int)(id->recv_count)-CDNS_I2C_FIFO_DEPTH) > CDNS_I2C_TRANSFER_SIZE){ cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE, CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE + CDNS_I2C_FIFO_DEPTH; } else { cdns_i2c_writereg(id->recv_count - CDNS_I2C_FIFO_DEPTH, CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = id->recv_count; } } else if (id->recv_count && !hold_quirk && !id->curr_recv_count) { /* Set the slave address in address register*/ cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK, CDNS_I2C_ADDR_OFFSET); if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) { cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE, CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE; } else { cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = id->recv_count; } } /* Clear hold (if not repeated start) and signal completion */ if ((isr_status & CDNS_I2C_IXR_COMP) && !id->recv_count) { if (!id->bus_hold_flag) cdns_i2c_clear_bus_hold(id); done_flag = 1; } status = IRQ_HANDLED; } /* When sending, handle transfer complete interrupt */ if ((isr_status & CDNS_I2C_IXR_COMP) && !id->p_recv_buf) { if (id->send_count) { avail_bytes = CDNS_I2C_FIFO_DEPTH - cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET); if (id->send_count > avail_bytes) bytes_to_send = avail_bytes; else bytes_to_send = id->send_count; while (bytes_to_send--) { cdns_i2c_writereg( (*(id->p_send_buf)++), CDNS_I2C_DATA_OFFSET); id->send_count--; } } else { done_flag = 1; } if (!id->send_count && !id->bus_hold_flag) cdns_i2c_clear_bus_hold(id); status = IRQ_HANDLED; } /* Handling Slave monitor mode interrupt */ if (isr_status & CDNS_I2C_IXR_SLV_RDY) { unsigned int ctrl_reg; /* Read control register */ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); /* Disable slave monitor mode */ ctrl_reg &= ~CDNS_I2C_CR_SLVMON; cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET); /* Clear interrupt flag for slvmon mode */ cdns_i2c_writereg(CDNS_I2C_IXR_SLV_RDY, CDNS_I2C_IDR_OFFSET); done_flag = 1; status = IRQ_HANDLED; } /* Save the error ID in CDNs_ i2c_ process_ Judgment processing in MSG */ id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK; if (id->err_status) status = IRQ_HANDLED; /*Complete CDNs for data processing_ i2c_ process_ Wait in MSG to judge whether this flag is set */ if (done_flag) complete(&id->xfer_done); return status; }
Process management;
cdns_i2c_probe # match to probe through device tree
Obtain equipment tree resources (terminal number, i2c base address, clock, etc.);
Initialize adapter structure
Initialize algorithm ID - > adap algo = &cdns_ i2c_ algo;
Registration interruption
Register adapter
What about device drivers? How to use adapter and algorithm?
Take a simple example:
static const struct i2c_device_id tmp401_id[] = { { "tmp401", tmp401 }, { "tmp411", tmp411 }, }; static struct i2c_driver tmp401_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "tmp401", .of_match_table = of_match_ptr(tmp401_of_match), }, .probe = tmp401_probe, .id_table = tmp401_id, .detect = tmp401_detect, .address_list = normal_i2c, };
Execute after matching: static int tmp401_probe(struct i2c_client *client, const struct i2c_device_id *id)
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; / / this is the one initialized in our controller. Use it to generate interrupt to send and receive data
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
Look at the probe function: it has been deleted
static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0); static const struct attribute_group tmp401_group = { .attrs = tmp401_attributes, }; static struct attribute *tmp401_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, NULL } static int tmp401_probe(struct i2c_client *client, const struct i2c_device_id *id) { static const char * const names[] = { "TMP401", "TMP411", "TMP431", "TMP432", "TMP435", "TMP461" }; struct device *dev = &client->dev; struct device *hwmon_dev; struct tmp401_data *data; int groups = 0, status; data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL); if (!data) return -ENOMEM; data->client = client; mutex_init(&data->update_lock); data->kind = id->driver_data; /* Initialize the TMP401 chip */ status = tmp401_init_client(data, client); if (status < 0) return status; /* Register sysfs hooks */ data->groups[groups++] = &tmp401_group; hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, data->groups); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); dev_info(dev, "Detected TI %s chip\n", names[data->kind]); return 0; }
Interface for reading temperature from the device:
static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute *devattr, char *buf) { int temp, index = to_sensor_dev_attr(devattr)->index; struct tmp401_data *data = tmp401_update_device(dev); if (IS_ERR(data)) return PTR_ERR(data); mutex_lock(&data->update_lock); temp = tmp401_register_to_temp(data->temp[3][index], data->config); temp -= data->temp_crit_hyst * 1000; mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", temp); } static struct tmp401_data *tmp401_update_device(struct device *dev) { struct tmp401_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; struct tmp401_data *ret = data; int i, val; unsigned long next_update; mutex_lock(&data->update_lock); next_update = data->last_updated + msecs_to_jiffies(data->update_interval); if (time_after(jiffies, next_update) || !data->valid) { if (data->kind != tmp432) { /* * The driver uses the TMP432 status format internally. * Convert status to TMP432 format for other chips. */ val = i2c_smbus_read_byte_data(client, TMP401_STATUS); if (val < 0) { ret = ERR_PTR(val); goto abort; } data->status[0] = (val & TMP401_STATUS_REMOTE_OPEN) >> 1; data->status[1] = ((val & TMP401_STATUS_REMOTE_LOW) >> 2) | ((val & TMP401_STATUS_LOCAL_LOW) >> 5); data->status[2] = ((val & TMP401_STATUS_REMOTE_HIGH) >> 3) | ((val & TMP401_STATUS_LOCAL_HIGH) >> 6); data->status[3] = val & (TMP401_STATUS_LOCAL_CRIT | TMP401_STATUS_REMOTE_CRIT); } else { for (i = 0; i < ARRAY_SIZE(data->status); i++) { val = i2c_smbus_read_byte_data(client, TMP432_STATUS_REG[i]); if (val < 0) { ret = ERR_PTR(val); goto abort; } data->status[i] = val; } } val = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); if (val < 0) { ret = ERR_PTR(val); goto abort; } data->config = val; val = tmp401_update_device_reg16(client, data); if (val < 0) { ret = ERR_PTR(val); goto abort; } val = i2c_smbus_read_byte_data(client, TMP401_TEMP_CRIT_HYST); if (val < 0) { ret = ERR_PTR(val); goto abort; } data->temp_crit_hyst = val; data->last_updated = jiffies; data->valid = 1; } abort: mutex_unlock(&data->update_lock); return ret; }
所有评论(0)