/*! * Dissociates the driver from the I2C device. * * @param pdev the device structure used to give information on which I2C * to remove * * @return The function always returns 0. */ static int mxci2c_remove(struct platform_device *pdev) { mxc_i2c_device *mxc_i2c = platform_get_drvdata(pdev); int id = pdev->id; free_irq(mxc_i2c->irq, mxc_i2c); i2c_del_adapter(&mxc_i2c->adap); gpio_i2c_inactive(id); clk_put(mxc_i2c->clk); platform_set_drvdata(pdev, NULL); return 0; }
/*! * This function is called to put the I2C adapter in a low power state. Refer to the * document driver-model/driver.txt in the kernel source tree for more * information. * * @param pdev the device structure used to give information on which I2C * to suspend * @param state the power state the device is entering * * @return The function returns 0 on success and -1 on failure. */ static int mxci2c_suspend(struct platform_device *pdev, pm_message_t state) { mxc_i2c_device *mxcdev = platform_get_drvdata(pdev); volatile unsigned int sr = 0; if (mxcdev == NULL) { return -1; } /* Prevent further calls to be processed */ mxcdev->low_power = true; /* Wait till we finish the current transfer */ sr = readw(mxcdev->membase + MXC_I2SR); while (sr & MXC_I2SR_IBB) { msleep(10); sr = readw(mxcdev->membase + MXC_I2SR); } gpio_i2c_inactive(mxcdev->adap.id); return 0; }
/*! * This function is called during the driver binding process. * * @param pdev the device structure used to store device specific * information that is used by the suspend, resume and remove * functions * * @return The function always returns 0. */ static int mxci2c_probe(struct platform_device *pdev) { mxc_i2c_device *mxc_i2c; struct mxc_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data; struct resource *res; int id = pdev->id; u32 clk_freq; int ret = 0; int i; mxc_i2c = kzalloc(sizeof(mxc_i2c_device), GFP_KERNEL); if (!mxc_i2c) { return -ENOMEM; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { ret = -ENODEV; goto err1; } mxc_i2c->membase = ioremap(res->start, res->end - res->start + 1); /* * Request the I2C interrupt */ mxc_i2c->irq = platform_get_irq(pdev, 0); if (mxc_i2c->irq < 0) { ret = mxc_i2c->irq; goto err2; } ret = request_irq(mxc_i2c->irq, mxc_i2c_handler, 0, pdev->name, mxc_i2c); if (ret < 0) { goto err2; } init_waitqueue_head(&mxc_i2c->wq); mxc_i2c->low_power = false; gpio_i2c_active(id); mxc_i2c->clk = clk_get(&pdev->dev, "i2c_clk"); clk_freq = clk_get_rate(mxc_i2c->clk); mxc_i2c->clkdiv = -1; if (i2c_plat_data->i2c_clk) { /* Calculate divider and round up any fractional part */ int div = (clk_freq + i2c_plat_data->i2c_clk - 1) / i2c_plat_data->i2c_clk; for (i = 0; i2c_clk_table[i].div != 0; i++) { if (i2c_clk_table[i].div >= div) { mxc_i2c->clkdiv = i2c_clk_table[i].reg_value; break; } } } if (mxc_i2c->clkdiv == -1) { i--; mxc_i2c->clkdiv = 0x1F; /* Use max divider */ } dev_dbg(&pdev->dev, "i2c speed is %d/%d = %d bps, reg val = 0x%02X\n", clk_freq, i2c_clk_table[i].div, clk_freq / i2c_clk_table[i].div, mxc_i2c->clkdiv); /* * Set the adapter information */ strlcpy(mxc_i2c->adap.name, pdev->name, 48); mxc_i2c->adap.id = mxc_i2c->adap.nr = id; mxc_i2c->adap.algo = &mxc_i2c_algorithm; mxc_i2c->adap.timeout = 1; platform_set_drvdata(pdev, mxc_i2c); i2c_set_adapdata(&mxc_i2c->adap, mxc_i2c); ret = i2c_add_numbered_adapter(&mxc_i2c->adap); if (ret < 0) goto err3; printk(KERN_INFO "MXC I2C driver\n"); return 0; err3: free_irq(mxc_i2c->irq, mxc_i2c); gpio_i2c_inactive(id); err2: iounmap(mxc_i2c->membase); err1: dev_err(&pdev->dev, "failed to probe i2c adapter\n"); kfree(mxc_i2c); return ret; }
int start_fx2lp_fw_dl(void) { unsigned int volatile sr = 0; unsigned int volatile cr = 0; unsigned int index = 0; unsigned int ret = 0; unsigned int offset = 8; char __iomem* fw; char __iomem* tmp; unsigned int fw_address = mot_usb_firmware_address(); unsigned int fw_size = mot_usb_firmware_size(); if(!fw_address || !fw_size) { printk(KERN_ERR "fx2lp: atag values look invalid\n"); return 1; } if(!(fw = ioremap(fw_address, fw_size))) { printk(KERN_ERR "fx2lp: unable to relocate memory holding firmware\n"); return 1; } DBG_PRINTK("fx2lp: firmware pointer is %p\n", fw); /* use this to "reset" our pointer when we get the address */ tmp = fw; /* set up mxc i2c bus */ writeb((I2C_SLAVE_ADDRESS << 1), I2C_MEMBASE + MXC_IADR); writeb(I2C_100KHZ_DIVIDER, I2C_MEMBASE + MXC_IFDR); writeb(I2C_CLEAR_STATUS_REG, I2C_MEMBASE + MXC_I2SR); gpio_i2c_active(I2C_NUM); mxc_clks_enable(I2C_CLK); /* slave mode is set by default */ writeb(MXC_I2CR_IEN, I2C_MEMBASE + MXC_I2CR); /* initialize EPIT hardware * - set 32K clock source and initial counter value to 0xffffffff */ writel(EPITCR_CLKSRC_32K | EPITCR_ENMOD, _reg_EPIT_EPITCR); next_timeout = EPIT_MAX - (FX2LP_RESET_TIMEOUT / MICROSECONDS_PER_TICK); mxc_clks_enable(EPIT1_CLK); DBG_PRINTK("fx2lp: reset chip and start download\n"); gpio_usb_hs_reset_set_data(TURN_CHIP_ON); /* start EPIT counter */ *_reg_EPIT_EPITCR |= EPITCR_EN; while(1) { sr = readb(I2C_MEMBASE + MXC_I2SR); if(MXC_I2SR_IIF & sr) { if(MXC_I2SR_IAL & sr) { /* arbitration lost? this is a problem */ printk(KERN_ERR "fx2lp: arbitration lost\n"); ret = 1; break; } else if(MXC_I2SR_IAAS & sr) { DBG_PRINTK("fx2lp: slave address match\n"); if(MXC_I2SR_SRW & sr) { /* slave write */ writeb(I2C_CLEAR_STATUS_REG, I2C_MEMBASE + MXC_I2SR); cr = readb(I2C_MEMBASE + MXC_I2CR); cr |= MXC_I2CR_MTX; writeb(cr, I2C_MEMBASE + MXC_I2CR); DBG_PRINTK("fx2lp:write:0x%02x\n", readb(fw + index)); /* initial write is in response to a probe and it doesn't really matter what we write for it */ writeb(readb(fw + index++), I2C_MEMBASE + MXC_I2DR); set_next_timeout(); } else { /* slave read */ writeb(I2C_CLEAR_STATUS_REG, I2C_MEMBASE + MXC_I2SR); cr = readb(I2C_MEMBASE + MXC_I2CR); cr &= ~MXC_I2CR_MTX; writeb(cr, I2C_MEMBASE + MXC_I2CR); DBG_PRINTK("fx2lp:reading first byte\n"); /* the fx2lp sends out 3 bytes...the first byte is a control byte that we don't care about and the last two bytes will contain our address */ (void)readb(I2C_MEMBASE + MXC_I2DR); index = 0; set_next_timeout(); } } else { /* transfer complete interrupt */ if(index == CONFIG_BYTE_400KHZ) { DBG_PRINTK("fx2lp: switching to 400kbps\n"); /* bit 0 in previous byte tells fx2lp to operate at 400kbps instead of 100kbps, the bit should be set to 1 */ writeb(I2C_400KHZ_DIVIDER, I2C_MEMBASE + MXC_IFDR); } writeb(I2C_CLEAR_STATUS_REG, I2C_MEMBASE + MXC_I2SR); cr = readb(I2C_MEMBASE + MXC_I2CR); if(MXC_I2CR_MTX & cr) { /* we're in transmit mode */ if(!(MXC_I2SR_RXAK & sr)) { /* ACK received from master */ DBG_PRINTK("fx2lp:write:0x%02x\n", readb(fw + index)); writeb(readb(fw + index++), I2C_MEMBASE + MXC_I2DR); set_next_timeout(); } else { DBG_PRINTK("fx2lp: NACK received from master\n"); /* give up i2c bus */ cr = readb(I2C_MEMBASE + MXC_I2CR); cr &= ~MXC_I2CR_MTX; writeb(cr, I2C_MEMBASE + MXC_I2CR); (void)readb(I2C_MEMBASE + MXC_I2DR); set_next_timeout(); /* index can be 1 after the probe so we need to check for a size greater than 1...when firmware download is complete the fx2lp will send a STOP */ if(index > 1) { /* last byte has been transferred */ DBG_PRINTK("fx2lp: wait for STOP\n"); do { sr = readb(I2C_MEMBASE + MXC_I2SR); } while ((MXC_I2SR_IBB & sr) && (next_timeout <= *_reg_EPIT_EPITCNR)); if(!(MXC_I2SR_IBB & sr)) { DBG_PRINTK("fx2lp: got the STOP\n"); } else { printk(KERN_ERR "fx2lp: i2c STOP timed out\n"); ret = 1; } break; } } } else { /* in receive mode */ /* the SCM-A11 is considered a two-byte addressable eeprom on the bus, grab the two bytes of address */ index |= (readb(I2C_MEMBASE + MXC_I2DR) << offset); if(!offset) { fw = tmp + index; DBG_PRINTK("fx2lp: index set to %0x\n", index); } /* update offset so we grab the next byte correctly */ offset = 0; set_next_timeout(); } } } else if(next_timeout >= *_reg_EPIT_EPITCNR) { if(index ? printk(KERN_ERR "fx2lp: i2c transfer timed out\n") : printk(KERN_ERR "fx2lp: chip reset timed out\n")); ret = 1; break; } } mxc_clks_disable(EPIT1_CLK); mxc_clks_disable(I2C_CLK); gpio_i2c_inactive(I2C_NUM); if(ret) { /* if firmware download didn't work then put the fx2lp back in reset */ gpio_usb_hs_reset_set_data(TURN_CHIP_OFF); } iounmap(fw); return ret; }