From: Charlie Paul <cpaul.windri...@gmail.com> Driver changes to support the AXXIA 55000 board.
Signed-off-by: Charlie Paul <cpaul.windri...@gmail.com> --- drivers/i2c/busses/Kconfig | 16 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/acp3400-i2c.c | 515 ++++++++++++++++++++++++++++++ drivers/i2c/busses/i2c-axxia.c | 657 ++++++++++++++++++++++++++++++++++++++ include/linux/i2c-axxia.h | 39 +++ 5 files changed, 1228 insertions(+) create mode 100644 drivers/i2c/busses/acp3400-i2c.c create mode 100644 drivers/i2c/busses/i2c-axxia.c create mode 100644 include/linux/i2c-axxia.h diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bfacb9e..783b22e 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1013,4 +1013,20 @@ config SCx200_ACB This support is also available as a module. If so, the module will be called scx200_acb. +config I2C_AXXIA + tristate "Axxia I2C bus support" + select I2C_ALGOBIT + help + Say yes if you want to support the I2C bus on Axxia platforms. + + If you don't know, say Y. + +config ACP3400_I2C + tristate "ACP3400 I2C support" + depends on ACP + default y + help + I2C adapter for acp476 based boards + + endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index a08931f..9711617 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o # Embedded system I2C/SMBus host controller drivers obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o +obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o diff --git a/drivers/i2c/busses/acp3400-i2c.c b/drivers/i2c/busses/acp3400-i2c.c new file mode 100644 index 0000000..b4febc0 --- /dev/null +++ b/drivers/i2c/busses/acp3400-i2c.c @@ -0,0 +1,515 @@ +/* + * ACP3400 I2C adapter + * + * Based on DU-TS I2C Adapter Driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/of_platform.h> +#include <linux/slab.h> + +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#define DRV_NAME "acp3400-i2c" + +struct acp3400_i2c_regs { + unsigned int txconfig; + unsigned int rxconfig; + unsigned int txstatus; + unsigned int rxstatus; + unsigned int irqenable; + unsigned int irqclear; + unsigned int irqstatus; + unsigned int clkconfig; + unsigned int startsetup; + unsigned int stopsetup; + unsigned int datasetup; + unsigned int bypassmode; + unsigned int slaveaddr; + unsigned int txdata0; + unsigned int txdata1; + unsigned int rxdata0; + unsigned int rxdata1; +}; + +struct acp3400_i2c_timer_regs { + unsigned int loadval; /* 0x20 */ + unsigned int val; /* 0x24 */ + unsigned int control; /* 0x28 */ + unsigned int irq_clr; /* 0x2c */ + unsigned int irq_stat_raw; /* 0x30 */ + unsigned int irq_stat; /* 0x34 */ + unsigned int bg_loadval; /* 0x38 */ +}; + + +/* Master Clock Configuration */ +/* Configured clock frequency i2c_freq = 100kHz. */ + +/* I2C register values */ +#define ACPI2C_CLK_100KHZ (1000 | (1000 << 16)) +#define ACPI2C_MSTSHC (940 | (800 << 16)) +#define ACPI2C_MSPSHC (800 | (0 << 16)) +#define ACPI2C_MDSHC (255 | (127 << 16)) + +#define ACPI2C_XFER_START 0x00000001 +#define ACPI2C_XFER_DONE 0x00000001 +#define ACPI2C_READ_MODE 0x00000200 +#define ACPI2C_STOP_MODE 0x20000000 +#define ACPI2C_MASTER_MODE 0x00000100 +#define ACPI2C_MASTER_OP_CLEAR 0x00000400 +#define ACPI2C_10BIT_ADDR 0x00000080 +#define ACPI2C_CLEAR_IRQ 0x0000000F +#define ACPI2C_DELAY 500 /* us */ +#define ACPI2C_RETRIES 100 +#define ACPI2C_REG_BSIZE 4 /* bytes */ +#define ACPI2C_DATA_REGS 2 + +struct acp3400_i2c { + struct device *dev; + struct i2c_adapter adap; + struct acp3400_i2c_regs __iomem *i2c_regs; + struct acp3400_i2c_timer_regs __iomem *timer_regs; + struct mutex i2c_lock; +}; + +#ifdef ACP3400_I2C_DEBUG +static void dump_regs(struct acp3400_i2c *i2c) +{ + pr_info("i2c-reg: txconfig %8.8x\n", + in_le32(&i2c->i2c_regs->txconfig)); + pr_info("i2c-reg: rxconfig %8.8x\n", + in_le32(&i2c->i2c_regs->rxconfig)); + pr_info("i2c-reg: txstatus %8.8x\n", + in_le32(&i2c->i2c_regs->txstatus)); + pr_info("i2c-reg: rxstatus %8.8x\n", + in_le32(&i2c->i2c_regs->rxstatus)); + pr_info("i2c-reg: irqenable %8.8x\n", + in_le32(&i2c->i2c_regs->irqenable)); + pr_info("i2c-reg: irqclear %8.8x\n", + in_le32(&i2c->i2c_regs->irqclear)); + pr_info("i2c-reg: irqstatus %8.8x\n", + in_le32(&i2c->i2c_regs->irqstatus)); + pr_info("i2c-reg: clkconfig %8.8x\n", + in_le32(&i2c->i2c_regs->clkconfig)); + pr_info("i2c-reg: startsetup %8.8x\n", + in_le32(&i2c->i2c_regs->startsetup)); + pr_info("i2c-reg: stopsetup %8.8x\n", + in_le32(&i2c->i2c_regs->stopsetup)); + pr_info("i2c-reg: datasetup %8.8x\n", + in_le32(&i2c->i2c_regs->datasetup)); + pr_info("i2c-reg: bypassmode %8.8x\n", + in_le32(&i2c->i2c_regs->bypassmode)); + pr_info("i2c-reg: slaveaddr %8.8x\n", + in_le32(&i2c->i2c_regs->slaveaddr)); + pr_info("i2c-reg: txdata0 %8.8x\n", + in_le32(&i2c->i2c_regs->txdata0)); + pr_info("i2c-reg: txdata1 %8.8x\n", + in_le32(&i2c->i2c_regs->txdata1)); + pr_info("i2c-reg: rxdata0 %8.8x\n", + in_le32(&i2c->i2c_regs->rxdata0)); + pr_info("i2c-reg: rxdata1 %8.8x\n", + in_le32(&i2c->i2c_regs->rxdata1)); + pr_info("i2c-timer-reg: loadval %8.8x\n", + in_le32(&i2c->timer_regs->loadval)); + pr_info("i2c-timer-reg: val %8.8x\n", + in_le32(&i2c->timer_regs->val)); + pr_info("i2c-timer-reg: control %8.8x\n", + in_le32(&i2c->timer_regs->control)); +} +#endif +/* + * Low level write routine + */ +static int acp3400_i2c_write_bytes(struct acp3400_i2c *i2c, + struct i2c_msg *msgs) +{ + unsigned char *bufp = msgs->buf; + unsigned int reg_value, data[2] = {0, 0}; + int cnt, ret = 0; + + if (msgs->len > (ACPI2C_REG_BSIZE * ACPI2C_DATA_REGS)) + msgs->len = ACPI2C_REG_BSIZE * ACPI2C_DATA_REGS; + + /* Set message */ + for (cnt = 0; cnt < msgs->len; cnt++) { + data[1] <<= 8; + data[1] |= ((data[0] >> 24) & 0xFF); + data[0] <<= 8; + data[0] |= bufp[cnt]; + } + out_le32(&i2c->i2c_regs->txdata0, data[0]); + out_le32(&i2c->i2c_regs->txdata1, data[1]); + + /* setup and start a transmission */ + reg_value = ACPI2C_MASTER_MODE | ACPI2C_STOP_MODE; + reg_value |= (msgs->len << 1) & 0x1e; + if (msgs->flags & I2C_M_TEN) { + reg_value |= ACPI2C_10BIT_ADDR; + /* TODO update slave address accordingly */ + } + out_le32(&i2c->i2c_regs->txconfig, reg_value); + + reg_value &= ~ACPI2C_STOP_MODE; + out_le32(&i2c->i2c_regs->txconfig, reg_value); + + reg_value |= ACPI2C_XFER_START; + out_le32(&i2c->i2c_regs->txconfig, reg_value); + + /* Check if the message has been sent + * Wait a totally of 1 s for the transmission */ + reg_value = cnt = 0; + while (0 == reg_value && cnt++ < ACPI2C_RETRIES) { + udelay(ACPI2C_DELAY); + /* Read transmission status */ + reg_value = in_le32(&i2c->i2c_regs->txstatus); + } +#ifdef ACP3400_I2C_DEBUG + if (ACPI2C_XFER_DONE != reg_value) + dump_regs(i2c); +#endif + /* Clear registers */ + out_le32(&i2c->i2c_regs->irqclear, ACPI2C_CLEAR_IRQ); + out_le32(&i2c->i2c_regs->txconfig, ACPI2C_MASTER_OP_CLEAR); + + out_le32(&i2c->i2c_regs->txconfig, ACPI2C_STOP_MODE); + + if (ACPI2C_XFER_DONE == reg_value) + ret = msgs->len; + else + ret = -EIO; + + return ret; +} + +/* + * Low level read routine + */ + +static int acp3400_i2c_read_bytes(struct acp3400_i2c *i2c, + struct i2c_msg *msgs) +{ + unsigned char *bufp = msgs->buf; + unsigned int reg_value, data[2]; + int cnt, ret = 0; + + if (msgs->len > (ACPI2C_REG_BSIZE * ACPI2C_DATA_REGS)) + msgs->len = ACPI2C_REG_BSIZE * ACPI2C_DATA_REGS; + + /* Setup a reception */ + reg_value = (msgs->len << 1) & 0x1e; + if (msgs->flags & I2C_M_TEN) { + reg_value |= ACPI2C_10BIT_ADDR; + /* TODO update slave address accordingly */ + } + out_le32(&i2c->i2c_regs->rxconfig, reg_value); + + /* set read mode and start clock */ + reg_value |= ACPI2C_XFER_START; + out_le32(&i2c->i2c_regs->rxconfig, reg_value); + + reg_value = ACPI2C_STOP_MODE | ACPI2C_MASTER_MODE | ACPI2C_READ_MODE; + out_le32(&i2c->i2c_regs->txconfig, reg_value); + + reg_value &= ~ACPI2C_STOP_MODE; + out_le32(&i2c->i2c_regs->txconfig, reg_value); + + reg_value |= ACPI2C_XFER_START; + out_le32(&i2c->i2c_regs->txconfig, reg_value); + + /* Check if the message has been received + * Wait a totally of 1 s for the reception */ + reg_value = cnt = 0; + while (0 == (ACPI2C_XFER_DONE & reg_value) && + cnt++ < ACPI2C_RETRIES) { + udelay(ACPI2C_DELAY); + /* Read transmission status */ + reg_value = in_le32(&i2c->i2c_regs->rxstatus); + } + + /* get message */ + data[0] = in_le32(&i2c->i2c_regs->rxdata0); + data[1] = in_le32(&i2c->i2c_regs->rxdata1); + for (cnt = 0; cnt < msgs->len; cnt++) { + if (cnt < ACPI2C_REG_BSIZE) + bufp[cnt] = data[0] >> ((8 * cnt) & 0xFF); + else + bufp[cnt] = data[1] >> + ((8 * (cnt - ACPI2C_REG_BSIZE)) & 0xFF); + } +#ifdef ACP3400_I2C_DEBUG + if (ACPI2C_XFER_DONE != (reg_value & 0x03)) + dump_regs(i2c); +#endif + /* clear registers */ + out_le32(&i2c->i2c_regs->irqclear, ACPI2C_CLEAR_IRQ); + out_le32(&i2c->i2c_regs->txconfig, ACPI2C_MASTER_OP_CLEAR); + + out_le32(&i2c->i2c_regs->txconfig, ACPI2C_STOP_MODE); + + if (ACPI2C_XFER_DONE == (reg_value & 0x03)) + ret = msgs->len; + else + ret = -EIO; + + return ret; +} + +/* + * I2C timer setup + */ +static void acp3400_i2c_timer_setup(struct acp3400_i2c *i2c) +{ + /* disable timer 1 */ + out_le32(&i2c->timer_regs->control, 0); + /* Program the Timer1 Load Value register with a value that sets + * the timer period to 250 ns (that is, 4 MHz frequency). When you + * configure the ACP peripheral clock (clk_per) for 200 MHz, the + * Timer1 Load Value is 0x31. */ + out_le32(&i2c->timer_regs->loadval, 0x31); + out_le32(&i2c->timer_regs->bg_loadval, 0x31); + + /* Configure and enable Timer1 for periodic wrapping mode + * with a prescaler of 1 by writing 0xc0 to the Timer1 Control + * Register. */ + out_le32(&i2c->timer_regs->control, 0xc0); +} +/* + * Low level master transfer routine + */ +static int acp3400_i2c_xfer_bytes(struct acp3400_i2c *i2c, + struct i2c_msg *msgs) +{ + int ret = 0; + + mutex_lock(&i2c->i2c_lock); + /* Prepare ACP3400 I2C for a transaction */ + out_le32(&i2c->i2c_regs->txconfig, + ACPI2C_MASTER_OP_CLEAR | ACPI2C_MASTER_MODE); + + out_le32(&i2c->i2c_regs->txconfig, + ACPI2C_MASTER_MODE | ACPI2C_STOP_MODE); + + /* I2C clock frequency and duty cycle */ + out_le32(&i2c->i2c_regs->clkconfig, ACPI2C_CLK_100KHZ); + /* The setup and hold durations for the START condition. */ + out_le32(&i2c->i2c_regs->startsetup, ACPI2C_MSTSHC); + /* The setup and hold durations for the STOP condition. */ + out_le32(&i2c->i2c_regs->stopsetup, ACPI2C_MSPSHC); + /* The setup and hold durations for the data bits. */ + out_le32(&i2c->i2c_regs->datasetup, ACPI2C_MDSHC); + /* Set Slave Address */ + out_le32(&i2c->i2c_regs->slaveaddr, msgs->addr); + /* Disable the actions for which the host requires to be interrupted */ + out_le32(&i2c->i2c_regs->irqenable, 0); + + /* Send/Receive Data */ + if (msgs->flags & I2C_M_RD) + ret = acp3400_i2c_read_bytes(i2c, msgs); + else + ret = acp3400_i2c_write_bytes(i2c, msgs); + + mutex_unlock(&i2c->i2c_lock); + return ret; +} +static void acp3400_i2c_dummy_xfer(struct acp3400_i2c *i2c) +{ + /* Prepare ACP3400 I2C for a transaction */ + out_le32(&i2c->i2c_regs->txconfig, + ACPI2C_MASTER_OP_CLEAR | ACPI2C_MASTER_MODE); + + out_le32(&i2c->i2c_regs->txconfig, + ACPI2C_MASTER_MODE | ACPI2C_STOP_MODE); + + /* I2C clock frequency and duty cycle */ + out_le32(&i2c->i2c_regs->clkconfig, ACPI2C_CLK_100KHZ); + /* The setup and hold durations for the START condition. */ + out_le32(&i2c->i2c_regs->startsetup, ACPI2C_MSTSHC); + /* The setup and hold durations for the STOP condition. */ + out_le32(&i2c->i2c_regs->stopsetup, ACPI2C_MSPSHC); + /* The setup and hold durations for the data bits. */ + out_le32(&i2c->i2c_regs->datasetup, ACPI2C_MDSHC); + /* Set Dummy Slave Address */ + out_le32(&i2c->i2c_regs->slaveaddr, 0x7f); + /* Disable the actions for which the host requires to be interrupted */ + out_le32(&i2c->i2c_regs->irqenable, 0); + /* Number of bytes 0, clear stop mode */ + out_le32(&i2c->i2c_regs->txconfig, ACPI2C_MASTER_MODE); + + /* Set Transmit Ready - triggers the transmit transaction */ + out_le32(&i2c->i2c_regs->txconfig, + (ACPI2C_XFER_START | ACPI2C_MASTER_MODE)); + +} + +/* + * Generic master transfer entrypoint. + * Returns the number of processed messages or error (<0) + */ +static int acp3400_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct acp3400_i2c *i2c = i2c_get_adapdata(adap); + int msg_cnt, ret = 0; + + if (!num) + return 0; + +#ifdef ACP3400_I2C_DEBUG + if (num == 1 && msgs[0].addr == 0x7f && msgs[0].len == 0) { + mutex_lock(&i2c->i2c_lock); + acp3400_i2c_dummy_xfer(i2c); + mutex_unlock(&i2c->i2c_lock); + return 0; + } +#endif + /* + * Check the sanity of the passed messages. + * Uhh, generic i2c layer is more suitable place for such code... + */ + if ((msgs[0].addr > 0x3ff) || + (!(msgs[0].flags & I2C_M_TEN) && (msgs[0].addr > 0x7f))) + return -EINVAL; + + for (msg_cnt = 0; msg_cnt < num; ++msg_cnt) { + if (msgs[msg_cnt].len <= 0) + return -EINVAL; + if ((msgs[0].addr != msgs[msg_cnt].addr) || + ((msgs[0].flags & I2C_M_TEN) != + (msgs[msg_cnt].flags & I2C_M_TEN))) + return -EINVAL; + } + + /* Do real transfer */ + for (msg_cnt = 0; msg_cnt < num; msg_cnt++) + ret = acp3400_i2c_xfer_bytes(i2c, &msgs[msg_cnt]); + return ret < 0 ? ret : num; +} + +static u32 acp3400_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm acp3400_i2c_algo = { + .master_xfer = acp3400_i2c_xfer, + .functionality = acp3400_i2c_functionality, +}; + +static struct i2c_adapter acp3400_i2c_ops = { + .owner = THIS_MODULE, + .name = "ACP3400 adapter", + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &acp3400_i2c_algo, + .timeout = HZ, +}; + +static int acp34xx_i2c_probe(struct platform_device *dev) +{ + struct device_node *np = dev->dev.of_node; + + struct acp3400_i2c *i2c; + int result = -ENODEV; + const u32 *field; + + if (!np) + return -ENODEV; + + field = of_get_property(np, "enabled", NULL); + if (!field || (field && (0 == *field))) + return -EINVAL; + + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + if (!i2c) + goto err; + + i2c->i2c_regs = of_iomap(np, 0); + i2c->timer_regs = of_iomap(np, 1); + if (!i2c->i2c_regs || !i2c->timer_regs) { + pr_err("%s: failed to map I/O\n", np->full_name); + goto err; + } + + i2c->adap = acp3400_i2c_ops; + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.of_node = of_node_get(np); + mutex_init(&i2c->i2c_lock); + + /* I2C timer setup */ + acp3400_i2c_timer_setup(i2c); + acp3400_i2c_dummy_xfer(i2c); + result = i2c_add_adapter(&i2c->adap); + if (result < 0) { + pr_err("%s: failed to add adapter\n", + np->full_name); + goto err; + } + + pr_info("%s: adapter has been added\n", np->full_name); + + dev_set_drvdata(&dev->dev, i2c); + return 0; +err: + if (i2c) { + if (i2c->i2c_regs) + iounmap(i2c->i2c_regs); + if (i2c->timer_regs) + iounmap(i2c->timer_regs); + kfree(i2c); + } + + return result; +} + + +static int acp34xx_i2c_remove(struct platform_device *dev) +{ + struct acp3400_i2c *i2c = dev_get_drvdata(&dev->dev); + + i2c_del_adapter(&i2c->adap); + kfree(i2c); + + return 0; +} + +static struct of_device_id acp_i2c_match[] = { + { + .compatible = "acp-i2c", + }, + { + .compatible = "acp,acp3400-i2c", + }, + { /* end of list */ }, +}; + +static struct platform_driver acp_i2c_driver = { + .driver = { + .name = "acp-i2c", + .owner = THIS_MODULE, + .of_match_table = acp_i2c_match, + }, + .probe = acp34xx_i2c_probe, + .remove = acp34xx_i2c_remove, +}; + +module_platform_driver(acp_i2c_driver); + +MODULE_AUTHOR("Andrey Panteleev <andrey.xx.pantel...@ericsson.com>"); +MODULE_DESCRIPTION("I2C adapter for ACP3400"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c new file mode 100644 index 0000000..97b1b5a --- /dev/null +++ b/drivers/i2c/busses/i2c-axxia.c @@ -0,0 +1,657 @@ +/* + * drivers/i2c/busses/i2c-axxia.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/module.h> + +#define SCL_WAIT_TIMEOUT_NS 25000000 +#define I2C_XFER_TIMEOUT (msecs_to_jiffies(500)) +#define I2C_STOP_TIMEOUT (msecs_to_jiffies(100)) +#define TX_FIFO_SIZE 8 +#define RX_FIFO_SIZE 8 + +struct i2c_regs { + __le32 global_control; + __le32 interrupt_status; + __le32 interrupt_enable; + __le32 wait_timer_control; + __le32 ibml_timeout; + __le32 ibml_low_mext; + __le32 ibml_low_sext; + __le32 timer_clock_div; + __le32 i2c_bus_monitor; + __le32 soft_reset; + __le32 mst_command; +#define CMD_MANUAL 0x08 +#define CMD_AUTO 0x09 + __le32 mst_rx_xfer; + __le32 mst_tx_xfer; + __le32 mst_addr_1; + __le32 mst_addr_2; + __le32 mst_data; + __le32 mst_tx_fifo; + __le32 mst_rx_fifo; + __le32 mst_int_enable; + __le32 mst_int_status; +#define MST_STATUS_RFL (1<<13) /* RX FIFO serivce */ +#define MST_STATUS_TFL (1<<12) /* TX FIFO service */ +#define MST_STATUS_SNS (1<<11) /* Manual mode done */ +#define MST_STATUS_SS (1<<10) /* Automatic mode done */ +#define MST_STATUS_SCC (1<<9) /* Stop complete */ +#define MST_STATUS_IP (1<<8) /* Invalid parameter */ +#define MST_STATUS_TSS (1<<7) /* Timeout */ +#define MST_STATUS_AL (1<<6) /* Arbitration lost */ +#define MST_STATUS_NAK (MST_STATUS_NA | MST_STATUS_ND) +#define MST_STATUS_ND (1<<5) /* NAK on data phase */ +#define MST_STATUS_NA (1<<4) /* NAK on address phase */ +#define MST_STATUS_ERR (MST_STATUS_NAK | \ + MST_STATUS_AL | \ + MST_STATUS_IP | \ + MST_STATUS_TSS) + __le32 mst_tx_bytes_xfrd; + __le32 mst_rx_bytes_xfrd; + __le32 slv_addr_dec_ctl; + __le32 slv_addr_1; + __le32 slv_addr_2; + __le32 slv_rx_ctl; + __le32 slv_data; + __le32 slv_rx_fifo; + __le32 slv_int_enable; + __le32 slv_int_status; + __le32 slv_read_dummy; + __le32 reserved; + __le32 scl_high_period; + __le32 scl_low_period; + __le32 spike_fltr_len; + __le32 sda_setup_time; + __le32 sda_hold_time; + __le32 smb_alert; + __le32 udid_w7; + __le32 udid_w6; + __le32 udid_w5; + __le32 udid_w4; + __le32 udid_w3; + __le32 udid_w2; + __le32 udid_w1; + __le32 udid_w0; + __le32 arppec_cfg_stat; + __le32 slv_arp_int_enable; + __le32 slv_arp_int_status; + __le32 mst_arp_int_enable; + __le32 mst_arp_int_status; +}; + + +/** + * I2C device context + */ +struct axxia_i2c_dev { + /** device reference */ + struct device *dev; + /** core i2c abstraction */ + struct i2c_adapter adapter; + /* clock reference for i2c input clock */ + struct clk *i2c_clk; + /* pointer to registers */ + struct i2c_regs __iomem *regs; + /* xfer completion object */ + struct completion msg_complete; + /* pointer to current message */ + struct i2c_msg *msg; + /* number of bytes transferred in msg */ + size_t msg_xfrd; + /* error code for completed message */ + int msg_err; + /* IRQ number (or 0 if not using interrupt) */ + int irq; + /* current i2c bus clock rate */ + u32 bus_clk_rate; +}; + +static void +i2c_int_disable(struct axxia_i2c_dev *idev, u32 mask) +{ + u32 int_mask = readl(&idev->regs->mst_int_enable); + int_mask &= ~mask; + writel(int_mask, &idev->regs->mst_int_enable); +} + +static void +i2c_int_enable(struct axxia_i2c_dev *idev, u32 mask) +{ + u32 int_mask = readl(&idev->regs->mst_int_enable); + int_mask |= mask; + writel(int_mask, &idev->regs->mst_int_enable); +} + +/** + * Convert nanoseconds to clock cycles for the given clock frequency. + */ +static u32 +ns_to_clk(u64 ns, u32 clk_mhz) +{ + return div_u64(ns*clk_mhz, 1000); +} + +static int +axxia_i2c_init(struct axxia_i2c_dev *idev) +{ + u32 divisor = clk_get_rate(idev->i2c_clk) / idev->bus_clk_rate; + u32 clk_mhz = clk_get_rate(idev->i2c_clk) / 1000000; + u32 t_setup; + u32 t_high, t_low; + u32 tmo_clk; + u32 prescale; + unsigned long timeout; + + dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n", + idev->bus_clk_rate, clk_mhz, divisor); + + /* Reset controller */ + writel(0x01, &idev->regs->soft_reset); + timeout = jiffies + msecs_to_jiffies(100); + while (readl(&idev->regs->soft_reset) & 1) { + if (time_after(jiffies, timeout)) { + dev_warn(idev->dev, "Soft reset failed\n"); + break; + } + } + + /* Enable Master Mode */ + writel(0x1, &idev->regs->global_control); + + if (idev->bus_clk_rate <= 100000) { + /* Standard mode SCL 50/50, tSU:DAT = 250 ns */ + t_high = divisor*1/2; + t_low = divisor*1/2; + t_setup = ns_to_clk(250, clk_mhz); + } else { + /* Fast mode SCL 33/66, tSU:DAT = 100 ns */ + t_high = divisor*1/3; + t_low = divisor*2/3; + t_setup = ns_to_clk(100, clk_mhz); + } + + /* SCL High Time */ + writel(t_high, &idev->regs->scl_high_period); + /* SCL Low Time */ + writel(t_low, &idev->regs->scl_low_period); + /* SDA Setup Time */ + writel(t_setup, &idev->regs->sda_setup_time); + /* SDA Hold Time, 300ns */ + writel(ns_to_clk(300, clk_mhz), &idev->regs->sda_hold_time); + /* Filter <50ns spikes */ + writel(ns_to_clk(50, clk_mhz), &idev->regs->spike_fltr_len); + + /* Configure Time-Out Registers */ + tmo_clk = ns_to_clk(SCL_WAIT_TIMEOUT_NS, clk_mhz); + + /* + Find the prescaler value that makes tmo_clk fit in 15-bits counter. + */ + for (prescale = 0; prescale < 15; ++prescale) { + if (tmo_clk <= 0x7fff) + break; + tmo_clk >>= 1; + } + if (tmo_clk > 0x7fff) + tmo_clk = 0x7fff; + + /* Prescale divider (log2) */ + writel(prescale, &idev->regs->timer_clock_div); + /* Timeout in divided clocks */ + writel((1<<15) | tmo_clk, &idev->regs->wait_timer_control); + + /* Mask all master interrupt bits */ + i2c_int_disable(idev, ~0); + + /* Interrupt enable */ + writel(0x01, &idev->regs->interrupt_enable); + + dev_dbg(idev->dev, "SDA_SETUP: %08x\n", + readl(&idev->regs->sda_setup_time)); + dev_dbg(idev->dev, "SDA_HOLD: %08x\n", + readl(&idev->regs->sda_hold_time)); + dev_dbg(idev->dev, "SPIKE_FILTER_LEN: %08x\n", + readl(&idev->regs->spike_fltr_len)); + dev_dbg(idev->dev, "TIMER_DIV: %08x\n", + readl(&idev->regs->timer_clock_div)); + dev_dbg(idev->dev, "WAIT_TIMER: %08x\n", + readl(&idev->regs->wait_timer_control)); + + return 0; +} + +static int +i2c_m_rd(const struct i2c_msg *msg) +{ + return (msg->flags & I2C_M_RD) != 0; +} + +static int +i2c_m_ten(const struct i2c_msg *msg) +{ + return (msg->flags & I2C_M_TEN) != 0; +} + +static int +i2c_m_recv_len(const struct i2c_msg *msg) +{ + return (msg->flags & I2C_M_RECV_LEN) != 0; +} + +static int +axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev) +{ + struct i2c_msg *msg = idev->msg; + size_t rx_fifo_avail = readl(&idev->regs->mst_rx_fifo); + int bytes_to_transfer = min(rx_fifo_avail, msg->len - idev->msg_xfrd); + + while (0 < bytes_to_transfer--) { + int c = readl(&idev->regs->mst_data); + if (idev->msg_xfrd == 0 && i2c_m_recv_len(msg)) { + /* + * Check length byte for SMBus block read + */ + if (c <= 0) { + idev->msg_err = -EPROTO; + i2c_int_disable(idev, ~0); + complete(&idev->msg_complete); + break; + } else if (c > I2C_SMBUS_BLOCK_MAX) { + c = I2C_SMBUS_BLOCK_MAX; + } + msg->len = 1 + c; + writel(msg->len, &idev->regs->mst_rx_xfer); + } + msg->buf[idev->msg_xfrd++] = c; + } + + return 0; +} + +static int +axxia_i2c_fill_tx_fifo(struct axxia_i2c_dev *idev) +{ + struct i2c_msg *msg = idev->msg; + size_t tx_fifo_avail = TX_FIFO_SIZE - readl(&idev->regs->mst_tx_fifo); + int bytes_to_transfer = min(tx_fifo_avail, msg->len - idev->msg_xfrd); + + while (0 < bytes_to_transfer--) + writel(msg->buf[idev->msg_xfrd++], &idev->regs->mst_data); + + return 0; +} + +static char * +status_str(u32 status) +{ + static char buf[128]; + + buf[0] = '\0'; + + if (status & MST_STATUS_RFL) + strcat(buf, "RFL "); + if (status & MST_STATUS_TFL) + strcat(buf, "TFL "); + if (status & MST_STATUS_SNS) + strcat(buf, "SNS "); + if (status & MST_STATUS_SS) + strcat(buf, "SS "); + if (status & MST_STATUS_SCC) + strcat(buf, "SCC "); + if (status & MST_STATUS_TSS) + strcat(buf, "TSS "); + if (status & MST_STATUS_AL) + strcat(buf, "AL "); + if (status & MST_STATUS_ND) + strcat(buf, "ND "); + if (status & MST_STATUS_NA) + strcat(buf, "NA "); + return buf; +} + +static void +axxia_i2c_service_irq(struct axxia_i2c_dev *idev) +{ + u32 status = readl(&idev->regs->mst_int_status); + + /* RX FIFO needs service? */ + if (i2c_m_rd(idev->msg) && (status & MST_STATUS_RFL)) + axxia_i2c_empty_rx_fifo(idev); + + /* TX FIFO needs service? */ + if (!i2c_m_rd(idev->msg) && (status & MST_STATUS_TFL)) { + if (idev->msg_xfrd < idev->msg->len) + axxia_i2c_fill_tx_fifo(idev); + else + i2c_int_disable(idev, MST_STATUS_TFL); + } + + if (status & MST_STATUS_SCC) { + /* Stop completed? */ + i2c_int_disable(idev, ~0); + complete(&idev->msg_complete); + } else if (status & (MST_STATUS_SNS | MST_STATUS_SS)) { + /* Transfer done? */ + if (i2c_m_rd(idev->msg) && idev->msg_xfrd < idev->msg->len) + axxia_i2c_empty_rx_fifo(idev); + i2c_int_disable(idev, ~0); + complete(&idev->msg_complete); + } else if (unlikely(status & MST_STATUS_ERR)) { + /* Transfer error? */ + idev->msg_err = status & MST_STATUS_ERR; + i2c_int_disable(idev, ~0); + dev_dbg(idev->dev, "error %s, rx=%u/%u tx=%u/%u\n", + status_str(status), + readl(&idev->regs->mst_rx_bytes_xfrd), + readl(&idev->regs->mst_rx_xfer), + readl(&idev->regs->mst_tx_bytes_xfrd), + readl(&idev->regs->mst_tx_xfer)); + complete(&idev->msg_complete); + } +} + +static irqreturn_t +axxia_i2c_isr(int irq, void *_dev) +{ + struct axxia_i2c_dev *idev = _dev; + + if ((readl(&idev->regs->interrupt_status) & 0x1) == 0) + return IRQ_NONE; + + if (!idev->msg) + return IRQ_NONE; + + axxia_i2c_service_irq(idev); + + /* Clear interrupt */ + writel(0x01, &idev->regs->interrupt_status); + + return IRQ_HANDLED; +} + + +static int +axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) +{ + u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS; + u32 addr_1, addr_2; + int ret; + + if (msg->len == 0 || msg->len > 255) + return -EINVAL; + + idev->msg = msg; + idev->msg_xfrd = 0; + idev->msg_err = 0; + reinit_completion(&idev->msg_complete); + + if (i2c_m_rd(msg)) { + /* TX 0 bytes */ + writel(0, &idev->regs->mst_tx_xfer); + /* RX # bytes */ + if (i2c_m_recv_len(msg)) + writel(I2C_SMBUS_BLOCK_MAX, &idev->regs->mst_rx_xfer); + else + writel(msg->len, &idev->regs->mst_rx_xfer); + } else { + /* TX # bytes */ + writel(msg->len, &idev->regs->mst_tx_xfer); + /* RX 0 bytes */ + writel(0, &idev->regs->mst_rx_xfer); + } + + if (i2c_m_ten(msg)) { + /* 10-bit address + * addr_1: 5'b11110 | addr[9:8] | (R/W) + * addr_2: addr[7:0] + */ + addr_1 = 0xF0 | ((msg->addr >> 7) & 0x06); + addr_2 = msg->addr & 0xFF; + } else { + /* 7-bit address + * addr_1: addr[6:0] | (R/W) + * addr_2: dont care + */ + addr_1 = (msg->addr << 1) & 0xFF; + addr_2 = 0; + } + if (i2c_m_rd(msg)) + addr_1 |= 1; + writel(addr_1, &idev->regs->mst_addr_1); + writel(addr_2, &idev->regs->mst_addr_2); + + if (i2c_m_rd(msg)) { + int_mask |= MST_STATUS_RFL; + } else { + axxia_i2c_fill_tx_fifo(idev); + if (idev->msg_xfrd < msg->len) + int_mask |= MST_STATUS_TFL; + } + + /* Start manual mode */ + writel(0x8, &idev->regs->mst_command); + + if (idev->irq > 0) { + i2c_int_enable(idev, int_mask); + ret = wait_for_completion_timeout(&idev->msg_complete, + I2C_XFER_TIMEOUT); + i2c_int_disable(idev, int_mask); + WARN_ON(readl(&idev->regs->mst_command) & 0x8); + } else { + unsigned long tmo = jiffies + I2C_XFER_TIMEOUT; + + do { + /* Poll interrupt status */ + axxia_i2c_service_irq(idev); + ret = try_wait_for_completion(&idev->msg_complete); + } while (!ret && time_before(jiffies, tmo)); + } + + if (ret == 0) { + dev_warn(idev->dev, "xfer timeout (%#x)\n", msg->addr); + axxia_i2c_init(idev); + return -ETIMEDOUT; + } + + if (unlikely(idev->msg_err != 0)) { + axxia_i2c_init(idev); + return -EIO; + } + + return 0; +} + +static int +axxia_i2c_stop(struct axxia_i2c_dev *idev) +{ + u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC; + int ret; + + reinit_completion(&idev->msg_complete); + + /* Issue stop */ + writel(0xb, &idev->regs->mst_command); + i2c_int_enable(idev, int_mask); + ret = wait_for_completion_timeout(&idev->msg_complete, + I2C_STOP_TIMEOUT); + i2c_int_disable(idev, int_mask); + if (ret == 0) + return -ETIMEDOUT; + + WARN_ON(readl(&idev->regs->mst_command) & 0x8); + + return 0; +} + +static int +axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct axxia_i2c_dev *idev = i2c_get_adapdata(adap); + int i; + int ret = 0; + + for (i = 0; ret == 0 && i < num; i++) + ret = axxia_i2c_xfer_msg(idev, &msgs[i]); + + axxia_i2c_stop(idev); + + return ret ?: i; +} + +static u32 +axxia_i2c_func(struct i2c_adapter *adap) +{ + u32 caps = (I2C_FUNC_I2C | + I2C_FUNC_10BIT_ADDR | + I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_BLOCK_DATA); + return caps; +} + +static const struct i2c_algorithm axxia_i2c_algo = { + .master_xfer = axxia_i2c_xfer, + .functionality = axxia_i2c_func, +}; + +static int +axxia_i2c_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct axxia_i2c_dev *idev = NULL; + struct resource *res; + void __iomem *base; + int ret = 0; + + idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL); + if (!idev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + idev->irq = platform_get_irq(pdev, 0); + if (idev->irq < 0) + dev_info(&pdev->dev, "No IRQ specified, using polling mode\n"); + + idev->i2c_clk = devm_clk_get(&pdev->dev, "i2c"); + if (IS_ERR(idev->i2c_clk)) { + dev_err(&pdev->dev, "missing I2C bus clock"); + return PTR_ERR(idev->i2c_clk); + } + + idev->regs = (struct i2c_regs __iomem *) base; + idev->dev = &pdev->dev; + init_completion(&idev->msg_complete); + + of_property_read_u32(np, "clock-frequency", &idev->bus_clk_rate); + if (idev->bus_clk_rate == 0) + idev->bus_clk_rate = 100000; /* default clock rate */ + + ret = axxia_i2c_init(idev); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize i2c controller"); + return ret; + } + + if (idev->irq >= 0) { + ret = devm_request_irq(&pdev->dev, idev->irq, axxia_i2c_isr, 0, + pdev->name, idev); + if (ret) { + dev_err(&pdev->dev, "can't claim irq %d\n", idev->irq); + return ret; + } + } + + clk_enable(idev->i2c_clk); + + i2c_set_adapdata(&idev->adapter, idev); + strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name)); + idev->adapter.owner = THIS_MODULE; + idev->adapter.class = I2C_CLASS_HWMON; + idev->adapter.algo = &axxia_i2c_algo; + idev->adapter.dev.parent = &pdev->dev; + idev->adapter.dev.of_node = pdev->dev.of_node; + + ret = i2c_add_adapter(&idev->adapter); + if (ret) { + dev_err(&pdev->dev, "Failed to add I2C adapter\n"); + return ret; + } + + platform_set_drvdata(pdev, idev); + + return 0; +} + +static int +axxia_i2c_remove(struct platform_device *pdev) +{ + struct axxia_i2c_dev *idev = platform_get_drvdata(pdev); + i2c_del_adapter(&idev->adapter); + return 0; +} + +#ifdef CONFIG_PM +static int axxia_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + return -EOPNOTSUPP; +} + +static int axxia_i2c_resume(struct platform_device *pdev) +{ + return -EOPNOTSUPP; +} +#else +#define axxia_i2c_suspend NULL +#define axxia_i2c_resume NULL +#endif + +/* Match table for of_platform binding */ +static const struct of_device_id axxia_i2c_of_match[] = { + { .compatible = "lsi,api2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, axxia_i2c_of_match); + +static struct platform_driver axxia_i2c_driver = { + .probe = axxia_i2c_probe, + .remove = axxia_i2c_remove, + .suspend = axxia_i2c_suspend, + .resume = axxia_i2c_resume, + .driver = { + .name = "axxia-i2c", + .owner = THIS_MODULE, + .of_match_table = axxia_i2c_of_match, + }, +}; + +module_platform_driver(axxia_i2c_driver); + +MODULE_DESCRIPTION("Axxia I2C Bus driver"); +MODULE_AUTHOR("Anders Berg <anders.b...@lsi.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/i2c-axxia.h b/include/linux/i2c-axxia.h new file mode 100644 index 0000000..435cd33 --- /dev/null +++ b/include/linux/i2c-axxia.h @@ -0,0 +1,39 @@ +#ifndef __I2C_AXXIA_H__ +#define __I2C_AXXIA_H__ + +#include <linux/platform_device.h> + +/* + * Version 2 of the I2C peripheral unit has a different register + * layout and extra registers. The ID register in the V2 peripheral + * unit on the AXXIA4430 reports the same ID as the V1 peripheral + * unit on the AXXIA3530, so we must inform the driver which IP + * version we know it is running on from platform / cpu-specific + * code using these constants in the hwmod class definition. + */ + +#define AXXIA_I2C_IP_VERSION_1 1 /* ACP34xx */ +#define AXXIA_I2C_IP_VERSION_2 2 /* AXM55xx */ + +/* struct axxia_i2c_bus_platform_data .flags meanings */ +#define AXXIA_I2C_FLAGS_NONE (0x00000000) + + +/* + * Maximum byte size of I2C bus name string including null terminator + */ +#define MAX_AXXIA_I2C_HWMOD_NAME_LEN 16 + + +struct axxia_i2c_bus_platform_data { + struct device_node *node; + char name[MAX_AXXIA_I2C_HWMOD_NAME_LEN]; + u32 index; + u32 rev; + u32 flags; + u32 bus_nr; + struct resource dev_space; + struct resource int_space; +}; + +#endif -- 1.7.9.5 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto