From: Heiko Schocher <hs@denx.de>
To: u-boot@lists.denx.de
Subject: [U-Boot] [RFC 2/2] dm: i2c: Add driver for Cadence I2C IP
Date: Mon, 4 Jan 2016 08:15:00 +0100 [thread overview]
Message-ID: <568A1BF4.7070002@denx.de> (raw)
In-Reply-To: <1451324831-18328-3-git-send-email-moritz.fischer@ettus.com>
Hello Moritz,
Am 28.12.2015 um 18:47 schrieb Moritz Fischer:
> This is a possible drop in replacement for drivers/i2c/zynq-i2c.c
>
> Since this is cadence IP it has been renamed to cdns-i2c,
> to make sense with the compatible string.
>
> Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
> ---
> drivers/i2c/Kconfig | 7 +
> drivers/i2c/Makefile | 1 +
> drivers/i2c/i2c-cdns.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 347 insertions(+)
> create mode 100644 drivers/i2c/i2c-cdns.c
Hmm.. I di not see the remove of "drivers/i2c/zynq-i2c.c" as
you wrote it is a replacement for it ... Can you do this, and
of course adapt the boards which use this driver?
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index 14adda2..c058dc5 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -58,6 +58,13 @@ config DM_I2C_GPIO
> bindings are supported.
> Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt
>
> +config SYS_I2C_CADENCE
> + tristate "Cadence I2C Controller"
> + depends on DM_I2C && (ARCH_ZYNQ || ARM64)
> + help
> + Say yes here to select Cadence I2C Host Controller. This controller is
> + e.g. used by Xilinx Zynq.
> +
> config SYS_I2C_ROCKCHIP
> bool "Rockchip I2C driver"
> depends on DM_I2C
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index 811ad9b..35ad0d3 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -16,6 +16,7 @@ obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o
> obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
> obj-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o
> obj-$(CONFIG_SYS_I2C) += i2c_core.o
> +obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o
> obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o
> obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o
> obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o
> diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
> new file mode 100644
> index 0000000..fab9609
> --- /dev/null
> +++ b/drivers/i2c/i2c-cdns.c
> @@ -0,0 +1,339 @@
> +/*
> + * Copyright (C) 2015 Moritz Fischer <moritz.fischer@ettus.com>
> + * IP from Cadence (ID T-CS-PE-0007-100, Version R1p10f2)
> + *
> + * This file is based on: drivers/i2c/zynq_i2c.c,
> + * with added driver-model support and code cleanup.
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <linux/types.h>
> +#include <linux/io.h>
> +#include <asm/errno.h>
> +#include <dm/device.h>
> +#include <dm/root.h>
> +#include <i2c.h>
> +#include <fdtdec.h>
> +#include <mapmem.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +/* i2c register set */
> +struct cdns_i2c_regs {
> + u32 control;
> + u32 status;
> + u32 address;
> + u32 data;
> + u32 interrupt_status;
> + u32 transfer_size;
> + u32 slave_mon_pause;
> + u32 time_out;
> + u32 interrupt_mask;
> + u32 interrupt_enable;
> + u32 interrupt_disable;
> +};
> +
> +/* Control register fields */
> +#define CDNS_I2C_CONTROL_RW 0x00000001
> +#define CDNS_I2C_CONTROL_MS 0x00000002
> +#define CDNS_I2C_CONTROL_NEA 0x00000004
> +#define CDNS_I2C_CONTROL_ACKEN 0x00000008
> +#define CDNS_I2C_CONTROL_HOLD 0x00000010
> +#define CDNS_I2C_CONTROL_SLVMON 0x00000020
> +#define CDNS_I2C_CONTROL_CLR_FIFO 0x00000040
> +#define CDNS_I2C_CONTROL_DIV_B_SHIFT 8
> +#define CDNS_I2C_CONTROL_DIV_B_MASK 0x00003F00
> +#define CDNS_I2C_CONTROL_DIV_A_SHIFT 14
> +#define CDNS_I2C_CONTROL_DIV_A_MASK 0x0000C000
> +
> +/* Status register values */
> +#define CDNS_I2C_STATUS_RXDV 0x00000020
> +#define CDNS_I2C_STATUS_TXDV 0x00000040
> +#define CDNS_I2C_STATUS_RXOVF 0x00000080
> +#define CDNS_I2C_STATUS_BA 0x00000100
> +
> +/* Interrupt register fields */
> +#define CDNS_I2C_INTERRUPT_COMP 0x00000001
> +#define CDNS_I2C_INTERRUPT_DATA 0x00000002
> +#define CDNS_I2C_INTERRUPT_NACK 0x00000004
> +#define CDNS_I2C_INTERRUPT_TO 0x00000008
> +#define CDNS_I2C_INTERRUPT_SLVRDY 0x00000010
> +#define CDNS_I2C_INTERRUPT_RXOVF 0x00000020
> +#define CDNS_I2C_INTERRUPT_TXOVF 0x00000040
> +#define CDNS_I2C_INTERRUPT_RXUNF 0x00000080
> +#define CDNS_I2C_INTERRUPT_ARBLOST 0x00000200
> +
> +#define CDNS_I2C_FIFO_DEPTH 16
> +#define CDNS_I2C_TRANSFER_SIZE_MAX 255 /* Controller transfer limit */
> +
> +#ifdef DEBUG
> +static void cdns_i2c_debug_status(struct cdns_i2c_regs *cdns_i2c)
> +{
> + int int_status;
> + int status;
> + int_status = readl(&cdns_i2c->interrupt_status);
> +
> + status = readl(&cdns_i2c->status);
> + if (int_status || status) {
> + debug("Status: ");
> + if (int_status & CDNS_I2C_INTERRUPT_COMP)
> + debug("COMP ");
> + if (int_status & CDNS_I2C_INTERRUPT_DATA)
> + debug("DATA ");
> + if (int_status & CDNS_I2C_INTERRUPT_NACK)
> + debug("NACK ");
> + if (int_status & CDNS_I2C_INTERRUPT_TO)
> + debug("TO ");
> + if (int_status & CDNS_I2C_INTERRUPT_SLVRDY)
> + debug("SLVRDY ");
> + if (int_status & CDNS_I2C_INTERRUPT_RXOVF)
> + debug("RXOVF ");
> + if (int_status & CDNS_I2C_INTERRUPT_TXOVF)
> + debug("TXOVF ");
> + if (int_status & CDNS_I2C_INTERRUPT_RXUNF)
> + debug("RXUNF ");
> + if (int_status & CDNS_I2C_INTERRUPT_ARBLOST)
> + debug("ARBLOST ");
> + if (status & CDNS_I2C_STATUS_RXDV)
> + debug("RXDV ");
> + if (status & CDNS_I2C_STATUS_TXDV)
> + debug("TXDV ");
> + if (status & CDNS_I2C_STATUS_RXOVF)
> + debug("RXOVF ");
> + if (status & CDNS_I2C_STATUS_BA)
> + debug("BA ");
> + debug("TS%d ", readl(&cdns_i2c->transfer_size));
> + debug("\n");
> + }
> +}
> +#endif
> +
> +struct i2c_cdns_bus {
> + int id;
> + struct cdns_i2c_regs __iomem *regs; /* register base */
> +};
> +
> +
> +/** cdns_i2c_probe() - Probe method
> + * @dev: udevice pointer
> + *
> + * DM callback called when device is probed
> + */
> +static int cdns_i2c_probe(struct udevice *dev)
> +{
> + struct i2c_cdns_bus *bus = dev_get_priv(dev);
> + fdt_addr_t addr;
> + fdt_size_t size;
> +
> + addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size);
> + bus->regs = map_sysmem(addr, size);
> +
> + if (!bus->regs)
> + return -ENOMEM;
> +
> + /* TODO: Calculate dividers based on CPU_CLK_1X */
> + /* 111MHz / ( (3 * 17) * 22 ) = ~100KHz */
> + writel((16 << CDNS_I2C_CONTROL_DIV_B_SHIFT) |
> + (2 << CDNS_I2C_CONTROL_DIV_A_SHIFT), &bus->regs->control);
We really need a clk framework ...
> +
> + /* Enable master mode, ack, and 7-bit addressing */
> + setbits_le32(&bus->regs->control, CDNS_I2C_CONTROL_MS |
> + CDNS_I2C_CONTROL_ACKEN | CDNS_I2C_CONTROL_NEA);
> +
> + debug("%s bus %d at %p\n", __func__, dev->seq, bus->regs);
> +
> + return 0;
> +}
> +
> +static int cdns_i2c_remove(struct udevice *dev)
> +{
> + struct i2c_cdns_bus *bus = dev_get_priv(dev);
> +
> + debug("%s bus %d at %p\n", __func__, dev->seq, bus->regs);
> +
> + unmap_sysmem(bus->regs);
> +
> + return 0;
> +}
> +
> +/* Wait for an interrupt */
> +static u32 cdns_i2c_wait(struct cdns_i2c_regs *cdns_i2c, u32 mask)
> +{
> + int timeout, int_status;
> +
> + for (timeout = 0; timeout < 100; timeout++) {
> + udelay(100);
> + int_status = readl(&cdns_i2c->interrupt_status);
> + if (int_status & mask)
> + break;
> + }
> +
> + /* Clear interrupt status flags */
> + writel(int_status & mask, &cdns_i2c->interrupt_status);
> +
> + return int_status & mask;
> +}
> +
> +static int cdns_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
> +{
> + if (speed != 100000) {
> + printf("%s, failed to set clock speed to %u\n", __func__,
> + speed);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +/* Probe to see if a chip is present. */
> +static int cdns_i2c_probe_chip(struct udevice *bus, uint chip_addr,
> + uint chip_flags)
> +{
> + struct i2c_cdns_bus *i2c_bus = dev_get_priv(bus);
> + struct cdns_i2c_regs *regs = i2c_bus->regs;
> +
> + /* Attempt to read a byte */
> + setbits_le32(®s->control, CDNS_I2C_CONTROL_CLR_FIFO |
> + CDNS_I2C_CONTROL_RW);
> + clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
> + writel(0xFF, ®s->interrupt_status);
> + writel(chip_addr, ®s->address);
> + writel(1, ®s->transfer_size);
> +
> + return (cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
> + CDNS_I2C_INTERRUPT_NACK) &
> + CDNS_I2C_INTERRUPT_COMP) ? 0 : -ETIMEDOUT;
> +}
> +
> +static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
> + u32 len, bool next_is_read)
> +{
> + u8 *cur_data = data;
> +
> + struct cdns_i2c_regs *regs = i2c_bus->regs;
> +
> + setbits_le32(®s->control, CDNS_I2C_CONTROL_CLR_FIFO |
> + CDNS_I2C_CONTROL_HOLD);
> +
> + /* if next is a read, we need to clear HOLD, doesn't work */
> + if (next_is_read)
> + clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
> +
> + clrbits_le32(®s->control, CDNS_I2C_CONTROL_RW);
> +
> + writel(0xFF, ®s->interrupt_status);
> + writel(addr, ®s->address);
> +
> + while (len--) {
> + writel(*(cur_data++), ®s->data);
> + if (readl(®s->transfer_size) == CDNS_I2C_FIFO_DEPTH) {
> + if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP)) {
> + /* Release the bus */
> + clrbits_le32(®s->control,
> + CDNS_I2C_CONTROL_HOLD);
> + return -ETIMEDOUT;
> + }
> + }
> + }
> +
> + /* All done... release the bus */
> + clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
> + /* Wait for the address and data to be sent */
> + if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP))
> + return -ETIMEDOUT;
> + return 0;
> +}
> +
> +static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
> + u32 len)
> +{
> + u32 status;
> + u32 i = 0;
> + u8 *cur_data = data;
> +
> + /* TODO: Fix this */
> + struct cdns_i2c_regs *regs = i2c_bus->regs;
> +
> + /* Check the hardware can handle the requested bytes */
> + if ((len < 0) || (len > CDNS_I2C_TRANSFER_SIZE_MAX))
> + return -EINVAL;
> +
> + setbits_le32(®s->control, CDNS_I2C_CONTROL_CLR_FIFO |
> + CDNS_I2C_CONTROL_RW);
> +
> + /* Start reading data */
> + writel(addr, ®s->address);
> + writel(len, ®s->transfer_size);
> +
> + /* Wait for data */
> + do {
> + status = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
> + CDNS_I2C_INTERRUPT_DATA);
> + if (!status) {
> + /* Release the bus */
> + clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
> + return -ETIMEDOUT;
> + }
> + debug("Read %d bytes\n",
> + len - readl(®s->transfer_size));
> + for (; i < len - readl(®s->transfer_size); i++)
> + *(cur_data++) = readl(®s->data);
> + } while (readl(®s->transfer_size) != 0);
> + /* All done... release the bus */
> + clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
> +
> +#ifdef DEBUG
> + cdns_i2c_debug_status(regs);
> +#endif
> + return 0;
> +}
> +
> +static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
> + int nmsgs)
> +{
> + struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
> + int ret;
> +
> + debug("i2c_xfer: %d messages\n", nmsgs);
> + for (; nmsgs > 0; nmsgs--, msg++) {
> + bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
> +
> + debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
> + if (msg->flags & I2C_M_RD) {
> + ret = cdns_i2c_read_data(i2c_bus, msg->addr, msg->buf,
> + msg->len);
> + } else {
> + ret = cdns_i2c_write_data(i2c_bus, msg->addr, msg->buf,
> + msg->len, next_is_read);
> + }
> + if (ret) {
> + debug("i2c_write: error sending\n");
> + return -EREMOTEIO;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static const struct dm_i2c_ops cdns_i2c_ops = {
> + .xfer = cdns_i2c_xfer,
> + .probe_chip = cdns_i2c_probe_chip,
> + .set_bus_speed = cdns_i2c_set_bus_speed,
> +};
> +
> +static const struct udevice_id cdns_i2c_of_match[] = {
> + { .compatible = "cdns,i2c-r1p10" },
> + { /* end of table */ }
> +};
> +
> +U_BOOT_DRIVER(cdns_i2c) = {
> + .name = "i2c-cdns",
> + .id = UCLASS_I2C,
> + .of_match = cdns_i2c_of_match,
> + .probe = cdns_i2c_probe,
> + .remove = cdns_i2c_remove,
> + .priv_auto_alloc_size = sizeof(struct i2c_cdns_bus),
> + .ops = &cdns_i2c_ops,
> +};
>
Looks good to me, but I cannot test it ...
bye,
Heiko
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
next prev parent reply other threads:[~2016-01-04 7:15 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-12-28 17:47 [U-Boot] [RFC 0/2] Add cdns-i2c driver as drop in for zynq-i2c Moritz Fischer
2015-12-28 17:47 ` [U-Boot] [RFC 1/2] i2c: Describe Cadence I2C devicetree bindings Moritz Fischer
2016-01-04 7:09 ` Heiko Schocher
2015-12-28 17:47 ` [U-Boot] [RFC 2/2] dm: i2c: Add driver for Cadence I2C IP Moritz Fischer
2016-01-04 7:15 ` Heiko Schocher [this message]
2016-04-11 12:50 ` Michal Simek
2016-04-12 7:45 ` Heiko Schocher
2016-04-12 7:59 ` Michal Simek
2015-12-28 18:35 ` [U-Boot] [RFC 0/2] Add cdns-i2c driver as drop in for zynq-i2c Michal Simek
2015-12-28 19:15 ` Moritz Fischer
2016-01-05 15:44 ` Michal Simek
2016-01-05 17:30 ` Moritz Fischer
2016-01-06 7:53 ` Michal Simek
2016-01-07 5:18 ` Moritz Fischer
2016-01-07 8:13 ` Michal Simek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=568A1BF4.7070002@denx.de \
--to=hs@denx.de \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.