Esempio n. 1
0
/*!
 * 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;
}
Esempio n. 2
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;
}
Esempio n. 3
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;
}
Esempio n. 4
0
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;
}