From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from exprod5og110.obsmtp.com ([64.18.0.20]) by merlin.infradead.org with smtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Urtcw-0002NZ-6w for barebox@lists.infradead.org; Wed, 26 Jun 2013 17:35:31 +0000 From: Renaud Barbier Date: Wed, 26 Jun 2013 18:33:47 +0100 Message-Id: <1372268027-15271-9-git-send-email-renaud.barbier@ge.com> In-Reply-To: <1372157137-7602-1-git-send-email-renaud.barbier@ge.com> References: <1372157137-7602-1-git-send-email-renaud.barbier@ge.com> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "barebox" Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: [PATCH 8/8] ppc 85xx: early I2C support To: barebox@lists.infradead.org Early I2C support is introduced to read SPD data from memory modules prior to the loading of the I2C driver. This code is based on the equivalent file fsl_i2c.c in directory drivers/i2c from U-Boot version git-a71d45d. Signed-off-by: Renaud Barbier --- 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 +#include +#include +#include +#include + +/* 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