Skip to content
Snippets Groups Projects
i2c-imx.c 49.9 KiB
Newer Older
// SPDX-License-Identifier: GPL-2.0+
Darius Augulis's avatar
Darius Augulis committed
/*
 *	Copyright (C) 2002 Motorola GSG-China
 *
 * Author:
 *	Darius Augulis, Teltonika Inc.
 *
 * Desc.:
 *	Implementation of I2C Adapter/Algorithm Driver
 *	for I2C Bus integrated in Freescale i.MX/MXC processors
 *
 *	Derived from Motorola GSG China I2C example driver
 *
 *	Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de
 *	Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de
 *	Copyright (C) 2007 RightHand Technologies, Inc.
 *	Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt>
 *
 *	Copyright 2013 Freescale Semiconductor, Inc.
 *	Copyright 2020 NXP
#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/completion.h>
Darius Augulis's avatar
Darius Augulis committed
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/dmapool.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
Darius Augulis's avatar
Darius Augulis committed
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
Darius Augulis's avatar
Darius Augulis committed
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/i2c-imx.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/libata.h>
Darius Augulis's avatar
Darius Augulis committed

/* This will be the driver name the kernel reports */
#define DRIVER_NAME "imx-i2c"

#define IMX_I2C_MAX_E_BIT_RATE	384000	/* 384kHz from e7805 errata*/

/*
 * Enable DMA if transfer byte size is bigger than this threshold.
 * As the hardware request, it must bigger than 4 bytes.\
 * I have set '16' here, maybe it's not the best but I think it's
 * the appropriate.
 */
#define DMA_THRESHOLD	16
#define DMA_TIMEOUT	1000

/* IMX I2C registers:
 * the I2C register offset is different between SoCs,
 * to provid support for all these chips, split the
 * register offset into a fixed base address and a
 * variable shift value, then the full register offset
 * will be calculated by
 * reg_off = ( reg_base_addr << reg_shift)
 */
Darius Augulis's avatar
Darius Augulis committed
#define IMX_I2C_IADR	0x00	/* i2c slave address */
#define IMX_I2C_IFDR	0x01	/* i2c frequency divider */
#define IMX_I2C_I2CR	0x02	/* i2c control */
#define IMX_I2C_I2SR	0x03	/* i2c status */
#define IMX_I2C_I2DR	0x04	/* i2c transfer data */

#define IMX_I2C_REGSHIFT	2
#define VF610_I2C_REGSHIFT	0
Darius Augulis's avatar
Darius Augulis committed

/* Bits of IMX I2C registers */
#define I2SR_RXAK	0x01
#define I2SR_IIF	0x02
#define I2SR_SRW	0x04
#define I2SR_IAL	0x10
#define I2SR_IBB	0x20
#define I2SR_IAAS	0x40
#define I2SR_ICF	0x80
#define I2CR_DMAEN	0x02
Darius Augulis's avatar
Darius Augulis committed
#define I2CR_RSTA	0x04
#define I2CR_TXAK	0x08
#define I2CR_MTX	0x10
#define I2CR_MSTA	0x20
#define I2CR_IIEN	0x40
#define I2CR_IEN	0x80

/* register bits different operating codes definition:
 * 1) I2SR: Interrupt flags clear operation differ between SoCs:
 * - write zero to clear(w0c) INT flag on i.MX,
 * - but write one to clear(w1c) INT flag on Vybrid.
 * 2) I2CR: I2C module enable operation also differ between SoCs:
 * - set I2CR_IEN bit enable the module on i.MX,
 * - but clear I2CR_IEN bit enable the module on Vybrid.
 */
#define I2SR_CLR_OPCODE_W0C	0x0
#define I2SR_CLR_OPCODE_W1C	(I2SR_IAL | I2SR_IIF)
#define I2CR_IEN_OPCODE_0	0x0
#define I2CR_IEN_OPCODE_1	I2CR_IEN

#define I2C_PM_TIMEOUT		1000 /* ms */
enum pinmux_endian_type {
	BIG_ENDIAN,
	LITTLE_ENDIAN,
};

struct pinmux_cfg {
	enum pinmux_endian_type endian; /* endian of RCWPMUXCR0 */
	u32 pmuxcr_offset;
	u32 pmuxcr_set_bit;		    /* pin mux of RCWPMUXCR0 */
};

static struct pinmux_cfg ls1012a_pinmux_cfg = {
	.endian = BIG_ENDIAN,
	.pmuxcr_offset = 0x430,
	.pmuxcr_set_bit = 0x10,
};

static struct pinmux_cfg ls1043a_pinmux_cfg = {
	.endian = BIG_ENDIAN,
	.pmuxcr_offset = 0x40C,
	.pmuxcr_set_bit = 0x10,
};

static struct pinmux_cfg ls1046a_pinmux_cfg = {
	.endian = BIG_ENDIAN,
	.pmuxcr_offset = 0x40C,
	.pmuxcr_set_bit = 0x80000000,
};

static const struct of_device_id pinmux_of_match[] = {
	{ .compatible = "fsl,ls1012a-vf610-i2c", .data = &ls1012a_pinmux_cfg},
	{ .compatible = "fsl,ls1043a-vf610-i2c", .data = &ls1043a_pinmux_cfg},
	{ .compatible = "fsl,ls1046a-vf610-i2c", .data = &ls1046a_pinmux_cfg},
	{},
};
MODULE_DEVICE_TABLE(of, pinmux_of_match);

/* The SCFG, Supplemental Configuration Unit, provides SoC specific
 * configuration and status registers for the device. There is a
 * SDHC IO VSEL control register on SCFG for some platforms. It's
 * used to support SDHC IO voltage switching.
 */
static const struct of_device_id scfg_device_ids[] = {
	{ .compatible = "fsl,ls1012a-scfg", },
	{ .compatible = "fsl,ls1043a-scfg", },
	{ .compatible = "fsl,ls1046a-scfg", },
	{}
};

Darius Augulis's avatar
Darius Augulis committed
/*
 * sorted list of clock divider, register value pairs
 * taken from table 26-5, p.26-9, Freescale i.MX
 * Integrated Portable System Processor Reference Manual
 * Document Number: MC9328MXLRM, Rev. 5.1, 06/2007
 *
 * Duplicated divider values removed from list
 */
struct imx_i2c_clk_pair {
	u16	div;
	u16	val;
};
static struct imx_i2c_clk_pair imx_i2c_clk_div[] = {
Darius Augulis's avatar
Darius Augulis committed
	{ 22,	0x20 }, { 24,	0x21 }, { 26,	0x22 }, { 28,	0x23 },
	{ 30,	0x00 },	{ 32,	0x24 }, { 36,	0x25 }, { 40,	0x26 },
	{ 42,	0x03 }, { 44,	0x27 },	{ 48,	0x28 }, { 52,	0x05 },
	{ 56,	0x29 }, { 60,	0x06 }, { 64,	0x2A },	{ 72,	0x2B },
	{ 80,	0x2C }, { 88,	0x09 }, { 96,	0x2D }, { 104,	0x0A },
	{ 112,	0x2E }, { 128,	0x2F }, { 144,	0x0C }, { 160,	0x30 },
	{ 192,	0x31 },	{ 224,	0x32 }, { 240,	0x0F }, { 256,	0x33 },
	{ 288,	0x10 }, { 320,	0x34 },	{ 384,	0x35 }, { 448,	0x36 },
	{ 480,	0x13 }, { 512,	0x37 }, { 576,	0x14 },	{ 640,	0x38 },
	{ 768,	0x39 }, { 896,	0x3A }, { 960,	0x17 }, { 1024,	0x3B },
	{ 1152,	0x18 }, { 1280,	0x3C }, { 1536,	0x3D }, { 1792,	0x3E },
	{ 1920,	0x1B },	{ 2048,	0x3F }, { 2304,	0x1C }, { 2560,	0x1D },
	{ 3072,	0x1E }, { 3840,	0x1F }
};

/* Vybrid VF610 clock divider, register value pairs */
static struct imx_i2c_clk_pair vf610_i2c_clk_div[] = {
	{ 20,   0x00 }, { 22,   0x01 }, { 24,   0x02 }, { 26,   0x03 },
	{ 28,   0x04 }, { 30,   0x05 }, { 32,   0x09 }, { 34,   0x06 },
Loading
Loading full blame...