mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Renaud Barbier <renaud.barbier@ge.com>
To: barebox@lists.infradead.org
Subject: [PATCH 8/8] ppc 85xx: early I2C support
Date: Tue, 25 Jun 2013 11:45:37 +0100	[thread overview]
Message-ID: <1372157137-7602-9-git-send-email-renaud.barbier@ge.com> (raw)
In-Reply-To: <1370019244-7873-1-git-send-email-renaud.barbier@ge.com>

Early I2C support is introduced to read SPD data from memory
modules prior to the loading of the I2C driver.

Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
 arch/ppc/mach-mpc85xx/fsl_i2c.c              |  253 ++++++++++++++++++++++++++
 arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h |   18 ++
 2 files changed, 271 insertions(+), 0 deletions(-)
 create mode 100644 arch/ppc/mach-mpc85xx/fsl_i2c.c
 create mode 100644 arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h

diff --git a/arch/ppc/mach-mpc85xx/fsl_i2c.c b/arch/ppc/mach-mpc85xx/fsl_i2c.c
new file mode 100644
index 0000000..51fcc64
--- /dev/null
+++ b/arch/ppc/mach-mpc85xx/fsl_i2c.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2013 GE Intelligent Platforms, Inc
+ * Copyright 2006,2009 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ * Early I2C support functions to read SPD data or board
+ * information.
+ * Based on U-Boot drivers/i2c/fsl_i2c.c
+ */
+#include <common.h>
+#include <i2c/i2c.h>
+#include <mach/clock.h>
+#include <mach/immap_85xx.h>
+#include <mach/early_udelay.h>
+
+/* FSL I2C registers */
+#define FSL_I2C_ADR	0x00
+#define FSL_I2C_FDR	0x04
+#define FSL_I2C_CR	0x08
+#define FSL_I2C_SR	0x0C
+#define FSL_I2C_DR	0x10
+#define FSL_I2C_DFSRR	0x14
+
+/* Bits of FSL I2C registers */
+#define I2C_SR_RXAK	0x01
+#define I2C_SR_MIF	0x02
+#define I2C_SR_MAL	0x10
+#define I2C_SR_MBB	0x20
+#define I2C_SR_MCF	0x80
+#define I2C_CR_RSTA	0x04
+#define I2C_CR_TXAK	0x08
+#define I2C_CR_MTX	0x10
+#define I2C_CR_MSTA	0x20
+#define I2C_CR_MEN	0x80
+
+/*
+ * Set the I2C bus speed for a given I2C device
+ *
+ * parameters:
+ * - i2c: the I2C base address.
+ * - i2c_clk: I2C bus clock frequency.
+ * - speed: the desired speed of the bus.
+ *
+ * The I2C device must be stopped before calling this function.
+ *
+ * The return value is the actual bus speed that is set.
+ */
+static uint32_t fsl_set_i2c_bus_speed(const void __iomem *i2c,
+		uint32_t i2c_clk, uint32_t speed)
+{
+	uint8_t dfsr, fdr = 0x31; /* Default if no FDR found */
+	uint16_t a, b, ga, gb, bin_gb, bin_ga, divider;
+	uint32_t c_div, est_div;
+
+	divider = min((uint16_t)(i2c_clk / speed), (uint16_t) -1);
+	/* Condition 1: dfsr <= 50/T */
+	dfsr = (5 * (i2c_clk / 1000)) / 100000;
+	if (!dfsr)
+		dfsr = 1;
+	est_div = ~0;
+
+	/*
+	 * Bus speed is calculated as per Freescale AN2919.
+	 * a, b and dfsr matches identifiers A,B and C as in the
+	 * application note.
+	 */
+	for (ga = 0x4, a = 10; a <= 30; ga++, a += 2) {
+		for (gb = 0; gb < 8; gb++) {
+			b = 16 << gb;
+			c_div = b * (a + (((3 * dfsr) / b) * 2));
+
+			if ((c_div > divider) && (c_div < est_div)) {
+				est_div = c_div;
+				bin_gb = gb << 2;
+				bin_ga = (ga & 0x3) | ((ga & 0x4) << 3);
+				fdr = bin_gb | bin_ga;
+				speed = i2c_clk / est_div;
+			}
+		}
+		if (a == 20)
+			a += 2;
+		if (a == 24)
+			a += 4;
+	}
+	writeb(dfsr, i2c + FSL_I2C_DFSRR);	/* set default filter */
+	writeb(fdr, i2c + FSL_I2C_FDR);		/* set bus speed */
+
+	return speed;
+}
+
+void fsl_i2c_init(void __iomem *i2c, int speed, int slaveadd)
+{
+	uint32_t i2c_clk;
+
+	i2c_clk =  fsl_get_i2c_freq();
+	writeb(0, i2c + FSL_I2C_CR);
+	early_udelay(5);
+
+	fsl_set_i2c_bus_speed(i2c, i2c_clk, speed);
+	writeb(slaveadd << 1, i2c + FSL_I2C_ADR);
+	writeb(0x0, i2c + FSL_I2C_SR);
+	writeb(I2C_CR_MEN, i2c + FSL_I2C_CR);
+}
+
+static uint32_t fsl_usec2ticks(uint32_t usec)
+{
+	ulong ticks;
+
+	if (usec < 1000) {
+		ticks = (usec * (fsl_get_timebase_clock() / 1000));
+		ticks = (ticks + 500) / 1000;
+	} else {
+		ticks = (usec / 10);
+		ticks *= (fsl_get_timebase_clock() / 100000);
+	}
+
+	return ticks;
+}
+
+static int fsl_i2c_wait4bus(void __iomem *i2c)
+{
+	uint64_t timeval = get_ticks();
+	const uint64_t timeout = fsl_usec2ticks(20000);
+
+	while (readb(i2c + FSL_I2C_SR) & I2C_SR_MBB)
+		if ((get_ticks() - timeval) > timeout)
+			return -1;
+
+	return 0;
+}
+
+void fsl_i2c_stop(void __iomem *i2c)
+{
+	writeb(I2C_CR_MEN, i2c + FSL_I2C_CR);
+}
+
+static int fsl_i2c_wait(void __iomem *i2c, int write)
+{
+	const uint64_t timeout = fsl_usec2ticks(100000);
+	uint64_t timeval = get_ticks();
+	int csr;
+
+	do {
+		csr = readb(i2c + FSL_I2C_SR);
+		if (csr & I2C_SR_MIF)
+			break;
+	} while ((get_ticks() - timeval) < timeout);
+
+	if ((get_ticks() - timeval) > timeout)
+		goto error;
+
+	csr = readb(i2c + FSL_I2C_SR);
+	writeb(0x0, i2c + FSL_I2C_SR);
+
+	if (csr & I2C_SR_MAL)
+		goto error;
+
+	if (!(csr & I2C_SR_MCF))
+		goto error;
+
+	if (write == 0 && (csr & I2C_SR_RXAK))
+		goto error;
+
+	return 0;
+error:
+	return -1;
+}
+
+static int __i2c_write(void __iomem *i2c, uint8_t *data, int length)
+{
+	int i;
+
+	for (i = 0; i < length; i++) {
+		writeb(data[i], i2c + FSL_I2C_DR);
+		if (fsl_i2c_wait(i2c, 0) < 0)
+			break;
+	}
+
+	return i;
+}
+
+static int __i2c_read(void __iomem *i2c, uint8_t *data, int length)
+{
+	int i;
+	uint8_t val = I2C_CR_MEN | I2C_CR_MSTA;
+
+	if (length == 1)
+		writeb(val | I2C_CR_TXAK, i2c + FSL_I2C_CR);
+	else
+		writeb(val, i2c + FSL_I2C_CR);
+
+	readb(i2c + FSL_I2C_DR);
+	for (i = 0; i < length; i++) {
+		if (fsl_i2c_wait(i2c, 1) < 0)
+			break;
+
+		/* Generate ack on last next to last byte */
+		if (i == length - 2)
+			writeb(val | I2C_CR_TXAK, i2c + FSL_I2C_CR);
+		/* Do not generate stop on last byte */
+		if (i == length - 1)
+			writeb(val | I2C_CR_MTX, i2c + FSL_I2C_CR);
+
+		data[i] = readb(i2c + FSL_I2C_DR);
+	}
+
+	return i;
+}
+
+static int
+fsl_i2c_write_addr(void __iomem *i2c, uint8_t dev, uint8_t dir, int rsta)
+{
+	uint8_t val = I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX;
+
+	if (rsta)
+		val |= I2C_CR_RSTA;
+	writeb(val, i2c + FSL_I2C_CR);
+	writeb((dev << 1) | dir, i2c + FSL_I2C_DR);
+
+	if (fsl_i2c_wait(i2c, 0) < 0)
+		return 0;
+
+	return 1;
+}
+
+int fsl_i2c_read(void __iomem *i2c, uint8_t dev, uint addr, int alen,
+			uint8_t *data, int length)
+{
+	int i = -1;
+	uint8_t *a = (uint8_t *)&addr;
+
+	if (alen && (fsl_i2c_wait4bus(i2c) >= 0) &&
+		(fsl_i2c_write_addr(i2c, dev, 0, 0) != 0) &&
+		(__i2c_write(i2c, &a[4 - alen], alen) == alen))
+		i = 0;
+
+	if (length && fsl_i2c_write_addr(i2c, dev, 1, 1) != 0)
+		i = __i2c_read(i2c, data, length);
+
+	if (i == length)
+		return 0;
+
+	return -1;
+}
diff --git a/arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h b/arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h
new file mode 100644
index 0000000..acf4cc7
--- /dev/null
+++ b/arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2013 GE Intelligent Platforms, Inc
+ *
+ * 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.
+ */
+void fsl_i2c_init(void __iomem *i2c, int speed, int slaveadd);
+int fsl_i2c_read(void __iomem *i2c, uint8_t dev, uint addr, int alen,
+		uint8_t *data, int length);
+void fsl_i2c_stop(void __iomem *i2c);
+
-- 
1.7.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

      parent reply	other threads:[~2013-06-25 10:47 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
2013-05-31 16:53 ` [PATCH 1/8] ppc 8xxx: DDR headers Renaud Barbier
2013-05-31 16:53 ` [PATCH 2/8] ppc 8xxx: memory controller register manipulation functions Renaud Barbier
2013-05-31 16:53 ` [PATCH 3/8] ppc 8xxx: dimm parameters calculation Renaud Barbier
2013-05-31 16:54 ` [PATCH 4/8] ppc 8xxx: lowest common dimm parameters Renaud Barbier
2013-05-31 16:54 ` [PATCH 5/8] ppc 8xxx: DDR utility functions Renaud Barbier
2013-05-31 16:54 ` [PATCH 6/8] ppc 8xxx: DDR specific options Renaud Barbier
2013-05-31 16:54 ` [PATCH 7/8] ppc 8xxx: core DDR driver functions Renaud Barbier
2013-06-02 15:44   ` Sascha Hauer
2013-06-12 14:51     ` Renaud Barbier
2013-05-31 16:54 ` [PATCH 8/8] ppc 8xxx: remove interactive debugging Renaud Barbier
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
2013-06-26  6:34   ` Sascha Hauer
2013-06-26 17:33   ` [PATCH v3 0/8] DDR2 memory initialisation Renaud Barbier
2013-06-27  6:39     ` Sascha Hauer
2013-06-26 17:33   ` [PATCH 1/8] common: DDR2 SPD checksum Renaud Barbier
2013-06-26 17:33   ` [PATCH 2/8] ppc asm: DDR headers Renaud Barbier
2013-06-26 17:33   ` [PATCH 3/8] ppc 8xxx: " Renaud Barbier
2013-06-26 17:33   ` [PATCH 4/8] ppc 8xxx: DIMM parameters calculation Renaud Barbier
2013-06-26 17:33   ` [PATCH 5/8] ppc 8xxx: DDR utility and memory options Renaud Barbier
2013-06-26 17:33   ` [PATCH 6/8] ppx 8xxx: DDR registers value calculation Renaud Barbier
2013-06-26 17:33   ` [PATCH 7/8] ppc 8xxx: core DDR driver functions Renaud Barbier
2013-06-26 17:33   ` [PATCH 8/8] ppc 85xx: early I2C support Renaud Barbier
2013-06-25 10:45 ` [PATCH 1/8] common: DDR2 SPD checksum Renaud Barbier
2013-06-26  6:26   ` Sascha Hauer
2013-06-25 10:45 ` [PATCH 2/8] ppc asm: DDR headers Renaud Barbier
2013-06-25 10:45 ` [PATCH 3/8] ppc 8xxx: " Renaud Barbier
2013-06-25 10:45 ` [PATCH 4/8] ppc 8xxx: DIMM parameters calculation Renaud Barbier
2013-06-25 10:45 ` [PATCH 5/8] ppc 8xxx: DDR utility and memory options Renaud Barbier
2013-06-25 10:45 ` [PATCH 6/8] ppx 8xxx: DDR registers value calculation Renaud Barbier
2013-06-25 10:45 ` [PATCH 7/8] ppc 8xxx: core DDR driver functions Renaud Barbier
2013-06-25 10:45 ` Renaud Barbier [this message]

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=1372157137-7602-9-git-send-email-renaud.barbier@ge.com \
    --to=renaud.barbier@ge.com \
    --cc=barebox@lists.infradead.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox