diff --git a/commands/i2c.c b/commands/i2c.c index 2811f6a..d6c5412 100644 --- a/commands/i2c.c +++ b/commands/i2c.c @@ -75,7 +75,7 @@ int addr = -1, reg = -1, count = -1, verbose = 0, ret, opt, i, bus = 0, wide = 0; u8 *buf; - while ((opt = getopt(argc, argv, "a:b:r:v:w")) > 0) { + while ((opt = getopt(argc, argv, "a:b:r:vw")) > 0) { switch (opt) { case 'a': addr = simple_strtol(optarg, NULL, 0); @@ -90,7 +90,7 @@ verbose = 1; break; case 'w': - wide = 1; + wide = I2C_ADDR_16_BIT; break; } } @@ -113,9 +113,13 @@ for (i = 0; i < count; i++) *(buf + i) = (char) simple_strtol(argv[optind+i], NULL, 16); - ret = i2c_write_reg(&client, reg | (wide ? I2C_ADDR_16_BIT : 0), buf, count); - if (ret != count) + ret = i2c_write_reg(&client, reg | wide, buf, count); + if (ret != count) { + if (verbose) + printf("write aborted, count(%i) != writestatus(%i)\n", + count, ret); goto out; + } ret = 0; if (verbose) { @@ -155,7 +159,7 @@ u8 *buf; int count = -1, addr = -1, reg = -1, verbose = 0, ret, opt, bus = 0, wide = 0; - while ((opt = getopt(argc, argv, "a:b:c:r:v:w")) > 0) { + while ((opt = getopt(argc, argv, "a:b:c:r:vw")) > 0) { switch (opt) { case 'a': addr = simple_strtol(optarg, NULL, 0); @@ -173,7 +177,7 @@ verbose = 1; break; case 'w': - wide = 1; + wide = I2C_ADDR_16_BIT; break; } } @@ -191,7 +195,7 @@ client.addr = addr; buf = xmalloc(count); - ret = i2c_read_reg(&client, reg | (wide ? I2C_ADDR_16_BIT : 0), buf, count); + ret = i2c_read_reg(&client, reg | wide, buf, count); if (ret == count) { int i; if (verbose) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 545c132..0ca7df4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -68,6 +68,26 @@ found on Marvell Orion and MVEBU SoCs (Armada 370/XP, Dove, Kirkwood, MV78x00, Orion5x). +config GPIO_PCA953X + bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports" + depends on I2C + help + Say yes here to provide access to several register-oriented + SMBus I/O expanders, made mostly by NXP or TI. Compatible + models include: + + 4 bits: pca9536, pca9537 + + 8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554, + pca9556, pca9557, pca9574, tca6408, xra1202 + + 16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575, + tca6416 + + 24 bits: tca6424 + + 40 bits: pca9505, pca9698 + config GPIO_PL061 bool "PrimeCell PL061 GPIO support" depends on ARM_AMBA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 045a18c..510d146 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_GPIO_MALTA_FPGA_I2C) += gpio-malta-fpga-i2c.o obj-$(CONFIG_GPIO_ORION) += gpio-orion.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o +obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c new file mode 100644 index 0000000..aabbb09 --- /dev/null +++ b/drivers/gpio/gpio-pca953x.c @@ -0,0 +1,481 @@ +/* + * PCA953x 4/8/16/24/40 bit I/O ports + * + * This code was ported from linux-3.15 kernel by Antony Pavlov. + * + * Copyright (C) 2005 Ben Gardner + * Copyright (C) 2007 Marvell International Ltd. + * + * Derived from drivers/i2c/chips/pca9539.c + * + * 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; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PCA953X_INPUT 0 +#define PCA953X_OUTPUT 1 +#define PCA953X_INVERT 2 +#define PCA953X_DIRECTION 3 + +#define REG_ADDR_AI 0x80 + +#define PCA957X_IN 0 +#define PCA957X_INVRT 1 +#define PCA957X_BKEN 2 +#define PCA957X_PUPD 3 +#define PCA957X_CFG 4 +#define PCA957X_OUT 5 +#define PCA957X_MSK 6 +#define PCA957X_INTS 7 + +#define PCA_GPIO_MASK 0x00FF +#define PCA_INT 0x0100 +#define PCA953X_TYPE 0x1000 +#define PCA957X_TYPE 0x2000 + +static struct platform_device_id pca953x_id[] = { + { "pca9505", 40 | PCA953X_TYPE | PCA_INT, }, + { "pca9534", 8 | PCA953X_TYPE | PCA_INT, }, + { "pca9535", 16 | PCA953X_TYPE | PCA_INT, }, + { "pca9536", 4 | PCA953X_TYPE, }, + { "pca9537", 4 | PCA953X_TYPE | PCA_INT, }, + { "pca9538", 8 | PCA953X_TYPE | PCA_INT, }, + { "pca9539", 16 | PCA953X_TYPE | PCA_INT, }, + { "pca9554", 8 | PCA953X_TYPE | PCA_INT, }, + { "pca9555", 16 | PCA953X_TYPE | PCA_INT, }, + { "pca9556", 8 | PCA953X_TYPE, }, + { "pca9557", 8 | PCA953X_TYPE, }, + { "pca9574", 8 | PCA957X_TYPE | PCA_INT, }, + { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, + { "pca9698", 40 | PCA953X_TYPE, }, + + { "max7310", 8 | PCA953X_TYPE, }, + { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, + { "max7313", 16 | PCA953X_TYPE | PCA_INT, }, + { "max7315", 8 | PCA953X_TYPE | PCA_INT, }, + { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, + { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, + { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, + { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, + { "xra1202", 8 | PCA953X_TYPE }, + { } +}; + +#define MAX_BANK 5 +#define BANK_SZ 8 + +#define NBANK(chip) (chip->gpio_chip.ngpio / BANK_SZ) + +struct pca953x_chip { + unsigned gpio_start; + u8 reg_output[MAX_BANK]; + u8 reg_direction[MAX_BANK]; + struct i2c_client *client; + struct gpio_chip gpio_chip; + const char *const *names; + int chip_type; +}; + +static inline struct pca953x_chip *to_pca(struct gpio_chip *gc) +{ + return container_of(gc, struct pca953x_chip, gpio_chip); +} + +static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val, + int off) +{ + int ret; + int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); + int offset = off / BANK_SZ; + + ret = i2c_smbus_read_byte_data(chip->client, + (reg << bank_shift) + offset); + *val = ret; + + if (ret < 0) { + dev_err(&chip->client->dev, "failed reading register\n"); + return ret; + } + + return 0; +} + +static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val, + int off) +{ + int ret = 0; + int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); + int offset = off / BANK_SZ; + + ret = i2c_smbus_write_byte_data(chip->client, + (reg << bank_shift) + offset, val); + + if (ret < 0) { + dev_err(&chip->client->dev, "failed writing register\n"); + return ret; + } + + return 0; +} + +static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val) +{ + int ret = 0; + + if (chip->gpio_chip.ngpio <= 8) + ret = i2c_smbus_write_byte_data(chip->client, reg, *val); + else if (chip->gpio_chip.ngpio >= 24) { + int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); + ret = i2c_smbus_write_i2c_block_data(chip->client, + (reg << bank_shift) | REG_ADDR_AI, + NBANK(chip), val); + } else { + switch (chip->chip_type) { + case PCA953X_TYPE: + ret = i2c_smbus_write_word_data(chip->client, + reg << 1, (u16) *val); + break; + case PCA957X_TYPE: + ret = i2c_smbus_write_byte_data(chip->client, reg << 1, + val[0]); + if (ret < 0) + break; + ret = i2c_smbus_write_byte_data(chip->client, + (reg << 1) + 1, + val[1]); + break; + } + } + + if (ret < 0) { + dev_err(&chip->client->dev, "failed writing register\n"); + return ret; + } + + return 0; +} + +static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val) +{ + int ret; + + if (chip->gpio_chip.ngpio <= 8) { + ret = i2c_smbus_read_byte_data(chip->client, reg); + *val = ret; + } else if (chip->gpio_chip.ngpio >= 24) { + int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); + + ret = i2c_smbus_read_i2c_block_data(chip->client, + (reg << bank_shift) | REG_ADDR_AI, + NBANK(chip), val); + } else { + ret = i2c_smbus_read_word_data(chip->client, reg << 1); + val[0] = (u16)ret & 0xFF; + val[1] = (u16)ret >> 8; + } + if (ret < 0) { + dev_err(&chip->client->dev, "failed reading register\n"); + return ret; + } + + return 0; +} + +static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct pca953x_chip *chip = to_pca(gc); + u8 reg_val; + int ret, offset = 0; + + reg_val = chip->reg_direction[off / BANK_SZ] | (1u << (off % BANK_SZ)); + + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_DIRECTION; + break; + case PCA957X_TYPE: + offset = PCA957X_CFG; + break; + } + ret = pca953x_write_single(chip, offset, reg_val, off); + if (ret) + goto exit; + + chip->reg_direction[off / BANK_SZ] = reg_val; + ret = 0; +exit: + return ret; +} + +static int pca953x_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + struct pca953x_chip *chip = to_pca(gc); + u8 reg_val; + int ret, offset = 0; + + /* set output level */ + if (val) + reg_val = chip->reg_output[off / BANK_SZ] + | (1u << (off % BANK_SZ)); + else + reg_val = chip->reg_output[off / BANK_SZ] + & ~(1u << (off % BANK_SZ)); + + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_OUTPUT; + break; + case PCA957X_TYPE: + offset = PCA957X_OUT; + break; + } + ret = pca953x_write_single(chip, offset, reg_val, off); + if (ret) + goto exit; + + chip->reg_output[off / BANK_SZ] = reg_val; + + /* then direction */ + reg_val = chip->reg_direction[off / BANK_SZ] & ~(1u << (off % BANK_SZ)); + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_DIRECTION; + break; + case PCA957X_TYPE: + offset = PCA957X_CFG; + break; + } + ret = pca953x_write_single(chip, offset, reg_val, off); + if (ret) + goto exit; + + chip->reg_direction[off / BANK_SZ] = reg_val; + ret = 0; +exit: + return ret; +} + +static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) +{ + struct pca953x_chip *chip = to_pca(gc); + u8 reg_val; + + reg_val = chip->reg_direction[off / BANK_SZ] & (1u << (off % BANK_SZ)); + + if (reg_val) + return GPIOF_DIR_IN; + + return GPIOF_DIR_OUT; +} + +static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct pca953x_chip *chip = to_pca(gc); + u32 reg_val; + int ret, offset = 0; + + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_INPUT; + break; + case PCA957X_TYPE: + offset = PCA957X_IN; + break; + } + ret = pca953x_read_single(chip, offset, ®_val, off); + if (ret < 0) { + /* NOTE: diagnostic already emitted; that's all we should + * do unless gpio_*_value_cansleep() calls become different + * from their nonsleeping siblings (and report faults). + */ + return 0; + } + + return (reg_val & (1u << (off % BANK_SZ))) ? 1 : 0; +} + +static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct pca953x_chip *chip = to_pca(gc); + u8 reg_val; + int ret, offset = 0; + + if (val) + reg_val = chip->reg_output[off / BANK_SZ] + | (1u << (off % BANK_SZ)); + else + reg_val = chip->reg_output[off / BANK_SZ] + & ~(1u << (off % BANK_SZ)); + + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_OUTPUT; + break; + case PCA957X_TYPE: + offset = PCA957X_OUT; + break; + } + ret = pca953x_write_single(chip, offset, reg_val, off); + if (ret) + goto exit; + + chip->reg_output[off / BANK_SZ] = reg_val; +exit: + return; +} + +static struct gpio_ops pca953x_gpio_ops = { + .direction_input = pca953x_gpio_direction_input, + .direction_output = pca953x_gpio_direction_output, + .get_direction = pca953x_gpio_get_direction, + .get = pca953x_gpio_get_value, + .set = pca953x_gpio_set_value, +}; + +static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) +{ + struct gpio_chip *gc; + + gc = &chip->gpio_chip; + + gc->ops = &pca953x_gpio_ops; + + gc->base = chip->gpio_start; + gc->ngpio = gpios; + gc->dev = &chip->client->dev; +} + +static int device_pca953x_init(struct pca953x_chip *chip, u32 invert) +{ + int ret; + u8 val[MAX_BANK]; + + ret = pca953x_read_regs(chip, PCA953X_OUTPUT, chip->reg_output); + if (ret) + goto out; + + ret = pca953x_read_regs(chip, PCA953X_DIRECTION, + chip->reg_direction); + if (ret) + goto out; + + /* set platform specific polarity inversion */ + if (invert) + memset(val, 0xFF, NBANK(chip)); + else + memset(val, 0, NBANK(chip)); + + ret = pca953x_write_regs(chip, PCA953X_INVERT, val); +out: + return ret; +} + +static int device_pca957x_init(struct pca953x_chip *chip, u32 invert) +{ + int ret; + u8 val[MAX_BANK]; + + ret = pca953x_read_regs(chip, PCA957X_OUT, chip->reg_output); + if (ret) + goto out; + ret = pca953x_read_regs(chip, PCA957X_CFG, chip->reg_direction); + if (ret) + goto out; + + /* set platform specific polarity inversion */ + if (invert) + memset(val, 0xFF, NBANK(chip)); + else + memset(val, 0, NBANK(chip)); + pca953x_write_regs(chip, PCA957X_INVRT, val); + + /* To enable register 6, 7 to controll pull up and pull down */ + memset(val, 0x02, NBANK(chip)); + pca953x_write_regs(chip, PCA957X_BKEN, val); + + return 0; +out: + return ret; +} + +static int pca953x_probe(struct device_d *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long driver_data; + struct pca953x_platform_data *pdata; + struct pca953x_chip *chip; + int ret; + u32 invert = 0; + + chip = xzalloc(sizeof(struct pca953x_chip)); + + driver_data = 0; + pdata = dev->platform_data; + if (pdata) { + chip->gpio_start = pdata->gpio_base; + invert = pdata->invert; + chip->names = pdata->names; + } else { + int err; + + err = dev_get_drvdata(dev, &driver_data); + if (err) + return err; + + chip->gpio_start = -1; + } + + chip->client = client; + + chip->chip_type = driver_data & (PCA953X_TYPE | PCA957X_TYPE); + + /* initialize cached registers from their original values. + * we can't share this chip with another i2c master. + */ + pca953x_setup_gpio(chip, driver_data & PCA_GPIO_MASK); + + if (chip->chip_type == PCA953X_TYPE) + ret = device_pca953x_init(chip, invert); + else + ret = device_pca957x_init(chip, invert); + if (ret) + return ret; + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + return ret; + + if (pdata && pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + return 0; +} + +static struct driver_d pca953x_driver = { + .name = "pca953x", + .probe = pca953x_probe, + .id_table = pca953x_id, +}; + +static int __init pca953x_init(void) +{ + return i2c_driver_register(&pca953x_driver); +} +device_initcall(pca953x_init); diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 5ce0324..648d844 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -1 +1 @@ -obj-$(CONFIG_I2C) += i2c.o busses/ algos/ +obj-$(CONFIG_I2C) += i2c.o i2c-smbus.o busses/ algos/ diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 370abb0..3962286 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -12,10 +12,22 @@ This is a very simple bitbanging I2C driver utilizing the arch-neutral GPIO API to control the SCL and SDA lines. +config I2C_AT91 + bool "AT91 I2C Master driver" + depends on ARCH_AT91 + config I2C_IMX bool "MPC85xx/i.MX I2C Master driver" depends on (ARCH_IMX && !ARCH_IMX1) || ARCH_MPC85XX +config I2C_MV64XXX + bool "Marvell mv64xxx I2C Controller" + depends on HAVE_CLK && OFDEVICE + help + If you say yes to this option, support will be included for the + built-in I2C interface on the Marvell 64xxx line of host bridges. + This driver is also used for Allwinner SoCs I2C controllers. + config I2C_OMAP bool "OMAP I2C Master driver" depends on ARCH_OMAP diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 9823d1b..1dbfbdf 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -1,5 +1,7 @@ +obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o -obj-$(CONFIG_I2C_IMX) += i2c-imx.o -obj-$(CONFIG_I2C_OMAP) += i2c-omap.o +obj-$(CONFIG_I2C_IMX) += i2c-imx.o +obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o +obj-$(CONFIG_I2C_OMAP) += i2c-omap.o obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c new file mode 100644 index 0000000..399f6a9 --- /dev/null +++ b/drivers/i2c/busses/i2c-at91.c @@ -0,0 +1,437 @@ +/* + * i2c Support for Atmel's AT91 Two-Wire Interface (TWI) + * + * Copyright (C) 2011 Weinmann Medical GmbH + * Author: Nikolaus Voss + * + * Evolved from original work by: + * Copyright (C) 2004 Rick Bronson + * Converted to 2.6 by Andrew Victor + * + * Borrowed heavily from original work by: + * Copyright (C) 2000 Philip Edelbrock + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */ +#define AT91_I2C_TIMEOUT (100 * MSECOND) /* transfer timeout */ +#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */ + +/* AT91 TWI register definitions */ +#define AT91_TWI_CR 0x0000 /* Control Register */ +#define AT91_TWI_START 0x0001 /* Send a Start Condition */ +#define AT91_TWI_STOP 0x0002 /* Send a Stop Condition */ +#define AT91_TWI_MSEN 0x0004 /* Master Transfer Enable */ +#define AT91_TWI_SVDIS 0x0020 /* Slave Transfer Disable */ +#define AT91_TWI_QUICK 0x0040 /* SMBus quick command */ +#define AT91_TWI_SWRST 0x0080 /* Software Reset */ + +#define AT91_TWI_MMR 0x0004 /* Master Mode Register */ +#define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */ +#define AT91_TWI_MREAD 0x1000 /* Master Read Direction */ + +#define AT91_TWI_IADR 0x000c /* Internal Address Register */ + +#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */ + +#define AT91_TWI_SR 0x0020 /* Status Register */ +#define AT91_TWI_TXCOMP 0x0001 /* Transmission Complete */ +#define AT91_TWI_RXRDY 0x0002 /* Receive Holding Register Ready */ +#define AT91_TWI_TXRDY 0x0004 /* Transmit Holding Register Ready */ + +#define AT91_TWI_OVRE 0x0040 /* Overrun Error */ +#define AT91_TWI_UNRE 0x0080 /* Underrun Error */ +#define AT91_TWI_NACK 0x0100 /* Not Acknowledged */ + +#define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */ +#define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */ +#define AT91_TWI_IMR 0x002c /* Interrupt Mask Register */ +#define AT91_TWI_RHR 0x0030 /* Receive Holding Register */ +#define AT91_TWI_THR 0x0034 /* Transmit Holding Register */ + +struct at91_twi_pdata { + unsigned clk_max_div; + unsigned clk_offset; + bool has_unre_flag; +}; + +struct at91_twi_dev { + struct device *dev; + void __iomem *base; + struct clk *clk; + u8 *buf; + size_t buf_len; + struct i2c_msg *msg; + unsigned imr; + unsigned transfer_status; + struct i2c_adapter adapter; + unsigned twi_cwgr_reg; + struct at91_twi_pdata *pdata; +}; + +#define to_at91_twi_dev(a) container_of(a, struct at91_twi_dev, adapter) + +static unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg) +{ + return __raw_readl(dev->base + reg); +} + +static void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val) +{ + __raw_writel(val, dev->base + reg); +} + +static void at91_disable_twi_interrupts(struct at91_twi_dev *dev) +{ + at91_twi_write(dev, AT91_TWI_IDR, + AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY); +} + +static void at91_init_twi_bus(struct at91_twi_dev *dev) +{ + at91_disable_twi_interrupts(dev); + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST); + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN); + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVDIS); + at91_twi_write(dev, AT91_TWI_CWGR, dev->twi_cwgr_reg); +} + +/* + * Calculate symmetric clock as stated in datasheet: + * twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset)) + */ +static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk) +{ + int ckdiv, cdiv, div; + struct at91_twi_pdata *pdata = dev->pdata; + int offset = pdata->clk_offset; + int max_ckdiv = pdata->clk_max_div; + + div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk), + 2 * twi_clk) - offset); + ckdiv = fls(div >> 8); + cdiv = div >> ckdiv; + + if (ckdiv > max_ckdiv) { + dev_warn(&dev->adapter.dev, "%d exceeds ckdiv max value which is %d.\n", + ckdiv, max_ckdiv); + ckdiv = max_ckdiv; + cdiv = 255; + } + + dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv; + dev_dbg(&dev->adapter.dev, "cdiv %d ckdiv %d\n", cdiv, ckdiv); +} + +static void at91_twi_write_next_byte(struct at91_twi_dev *dev) +{ + if (dev->buf_len <= 0) + return; + + at91_twi_write(dev, AT91_TWI_THR, *dev->buf); + + /* send stop when last byte has been written */ + if (--dev->buf_len == 0) + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); + + dev_dbg(&dev->adapter.dev, "wrote 0x%x, to go %d\n", *dev->buf, dev->buf_len); + + ++dev->buf; +} + +static void at91_twi_read_next_byte(struct at91_twi_dev *dev) +{ + if (dev->buf_len <= 0) + return; + + *dev->buf = at91_twi_read(dev, AT91_TWI_RHR) & 0xff; + --dev->buf_len; + + /* send stop if second but last byte has been read */ + if (dev->buf_len == 1) + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); + + dev_dbg(&dev->adapter.dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len); + + ++dev->buf; +} + +static int at91_twi_wait_completion(struct at91_twi_dev *dev) +{ + uint64_t start = get_time_ns(); + unsigned int status = at91_twi_read(dev, AT91_TWI_SR); + unsigned int irqstatus = at91_twi_read(dev, AT91_TWI_IMR); + + if (irqstatus & AT91_TWI_RXRDY) + at91_twi_read_next_byte(dev); + else if (irqstatus & AT91_TWI_TXRDY) + at91_twi_write_next_byte(dev); + else + dev_warn(&dev->adapter.dev, "neither rx and tx are ready\n"); + + dev->transfer_status |= status; + + while(!(at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_TXCOMP)) { + if(is_timeout(start, AT91_I2C_TIMEOUT)) { + dev_warn(&dev->adapter.dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + } + + at91_disable_twi_interrupts(dev); + + return 0; +} + +static int at91_do_twi_transfer(struct at91_twi_dev *dev) +{ + int ret; + bool has_unre_flag = dev->pdata->has_unre_flag; + + dev_dbg(&dev->adapter.dev, "transfer: %s %d bytes.\n", + (dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len); + + dev->transfer_status = 0; + + if (!dev->buf_len) { + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_QUICK); + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); + } else if (dev->msg->flags & I2C_M_RD) { + unsigned start_flags = AT91_TWI_START; + + if (at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_RXRDY) { + dev_err(&dev->adapter.dev, "RXRDY still set!"); + at91_twi_read(dev, AT91_TWI_RHR); + } + + /* if only one byte is to be read, immediately stop transfer */ + if (dev->buf_len <= 1) + start_flags |= AT91_TWI_STOP; + + at91_twi_write(dev, AT91_TWI_CR, start_flags); + + at91_twi_write(dev, AT91_TWI_IER, + AT91_TWI_TXCOMP | AT91_TWI_RXRDY); + } else { + at91_twi_write_next_byte(dev); + at91_twi_write(dev, AT91_TWI_IER, + AT91_TWI_TXCOMP | AT91_TWI_TXRDY); + } + + ret = at91_twi_wait_completion(dev); + if (ret < 0) { + dev_err(&dev->adapter.dev, "controller timed out\n"); + at91_init_twi_bus(dev); + ret = -ETIMEDOUT; + goto error; + } + if (dev->transfer_status & AT91_TWI_NACK) { + dev_dbg(&dev->adapter.dev, "received nack\n"); + ret = -EREMOTEIO; + goto error; + } + if (dev->transfer_status & AT91_TWI_OVRE) { + dev_err(&dev->adapter.dev, "overrun while reading\n"); + ret = -EIO; + goto error; + } + if (has_unre_flag && dev->transfer_status & AT91_TWI_UNRE) { + dev_err(&dev->adapter.dev, "underrun while writing\n"); + ret = -EIO; + goto error; + } + dev_dbg(&dev->adapter.dev, "transfer complete\n"); + + return 0; + +error: + return ret; +} + +static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) +{ + struct at91_twi_dev *dev = to_at91_twi_dev(adap); + int ret; + unsigned int_addr_flag = 0; + struct i2c_msg *m_start = msg; + + dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num); + + /* + * The hardware can handle at most two messages concatenated by a + * repeated start via it's internal address feature. + */ + if (num > 2) { + dev_err(&dev->adapter.dev, + "cannot handle more than two concatenated messages.\n"); + return 0; + } else if (num == 2) { + int internal_address = 0; + int i; + + if (msg->flags & I2C_M_RD) { + dev_err(&dev->adapter.dev, "first transfer must be write.\n"); + return -EINVAL; + } + if (msg->len > 3) { + dev_err(&dev->adapter.dev, "first message size must be <= 3.\n"); + return -EINVAL; + } + + /* 1st msg is put into the internal address, start with 2nd */ + m_start = &msg[1]; + for (i = 0; i < msg->len; ++i) { + const unsigned addr = msg->buf[msg->len - 1 - i]; + + internal_address |= addr << (8 * i); + int_addr_flag += AT91_TWI_IADRSZ_1; + } + at91_twi_write(dev, AT91_TWI_IADR, internal_address); + } + + at91_twi_write(dev, AT91_TWI_MMR, (m_start->addr << 16) | int_addr_flag + | ((m_start->flags & I2C_M_RD) ? AT91_TWI_MREAD : 0)); + + dev->buf_len = m_start->len; + dev->buf = m_start->buf; + dev->msg = m_start; + + ret = at91_do_twi_transfer(dev); + + return (ret < 0) ? ret : num; +} + +static struct at91_twi_pdata at91rm9200_config = { + .clk_max_div = 5, + .clk_offset = 3, + .has_unre_flag = true, +}; + +static struct at91_twi_pdata at91sam9261_config = { + .clk_max_div = 5, + .clk_offset = 4, + .has_unre_flag = false, +}; + +static struct at91_twi_pdata at91sam9260_config = { + .clk_max_div = 7, + .clk_offset = 4, + .has_unre_flag = false, +}; + +static struct at91_twi_pdata at91sam9g20_config = { + .clk_max_div = 7, + .clk_offset = 4, + .has_unre_flag = false, +}; + +static struct at91_twi_pdata at91sam9g10_config = { + .clk_max_div = 7, + .clk_offset = 4, + .has_unre_flag = false, +}; + +static struct platform_device_id at91_twi_devtypes[] = { + { + .name = "i2c-at91rm9200", + .driver_data = (unsigned long) &at91rm9200_config, + }, { + .name = "i2c-at91sam9261", + .driver_data = (unsigned long) &at91sam9261_config, + }, { + .name = "i2c-at91sam9260", + .driver_data = (unsigned long) &at91sam9260_config, + }, { + .name = "i2c-at91sam9g20", + .driver_data = (unsigned long) &at91sam9g20_config, + }, { + .name = "i2c-at91sam9g10", + .driver_data = (unsigned long) &at91sam9g10_config, + }, { + /* sentinel */ + } +}; + +static int at91_twi_probe(struct device_d *dev) +{ + struct at91_twi_dev *i2c_at91; + struct at91_twi_pdata *i2c_data; + int rc; + u32 bus_clk_rate; + + i2c_at91 = xzalloc(sizeof(struct at91_twi_dev)); + + rc = dev_get_drvdata(dev, (unsigned long *)&i2c_data); + if (rc) + goto out_free; + + i2c_at91->pdata = i2c_data; + + i2c_at91->base = dev_request_mem_region(dev, 0); + if (!i2c_at91->base) { + dev_err(dev, "could not get memory region\n"); + rc = -ENODEV; + goto out_free; + } + + i2c_at91->clk = clk_get(dev, "twi_clk"); + if (IS_ERR(i2c_at91->clk)) { + dev_err(dev, "no clock defined\n"); + rc = -ENODEV; + goto out_free; + } + + clk_enable(i2c_at91->clk); + + bus_clk_rate = DEFAULT_TWI_CLK_HZ; + + at91_calc_twi_clock(i2c_at91, bus_clk_rate); + at91_init_twi_bus(i2c_at91); + + i2c_at91->adapter.master_xfer = at91_twi_xfer; + i2c_at91->adapter.dev.parent = dev; + i2c_at91->adapter.nr = dev->id; + i2c_at91->adapter.dev.device_node = dev->device_node; + + rc = i2c_add_numbered_adapter(&i2c_at91->adapter); + if (rc) { + dev_err(dev, "Failed to add I2C adapter\n"); + goto out_adap_fail; + } + + dev_info(dev, "AT91 i2c bus driver.\n"); + return 0; + +out_adap_fail: + clk_disable(i2c_at91->clk); + clk_put(i2c_at91->clk); +out_free: + kfree(i2c_at91); + return rc; +} + +static struct driver_d at91_twi_driver = { + .name = "at91-twi", + .probe = at91_twi_probe, + .id_table = at91_twi_devtypes, +}; +device_platform_driver(at91_twi_driver); + +MODULE_AUTHOR("Nikolaus Voss "); +MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c new file mode 100644 index 0000000..6d8c85b --- /dev/null +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -0,0 +1,647 @@ +/* + * Driver for the i2c controller on the Marvell line of host bridges + * (e.g, gt642[46]0, mv643[46]0, mv644[46]0, and Orion SoC family). + * + * This code was ported from linux-3.15 kernel by Antony Pavlov. + * + * Author: Mark A. Greer + * + * 2005 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ADDR_ADDR(val) ((val & 0x7f) << 1) +#define BAUD_DIV_N(val) (val & 0x7) +#define BAUD_DIV_M(val) ((val & 0xf) << 3) + +#define REG_CONTROL_ACK 0x00000004 +#define REG_CONTROL_IFLG 0x00000008 +#define REG_CONTROL_STOP 0x00000010 +#define REG_CONTROL_START 0x00000020 +#define REG_CONTROL_TWSIEN 0x00000040 +#define REG_CONTROL_INTEN 0x00000080 + +/* Ctlr status values */ +#define STATUS_MAST_START 0x08 +#define STATUS_MAST_REPEAT_START 0x10 +#define STATUS_MAST_WR_ADDR_ACK 0x18 +#define STATUS_MAST_WR_ADDR_NO_ACK 0x20 +#define STATUS_MAST_WR_ACK 0x28 +#define STATUS_MAST_WR_NO_ACK 0x30 +#define STATUS_MAST_RD_ADDR_ACK 0x40 +#define STATUS_MAST_RD_ADDR_NO_ACK 0x48 +#define STATUS_MAST_RD_DATA_ACK 0x50 +#define STATUS_MAST_RD_DATA_NO_ACK 0x58 +#define STATUS_MAST_WR_ADDR_2_ACK 0xd0 +#define STATUS_MAST_RD_ADDR_2_ACK 0xe0 + +/* Driver states */ +enum mv64xxx_state { + STATE_INVALID, + STATE_IDLE, + STATE_WAITING_FOR_START_COND, + STATE_WAITING_FOR_RESTART, + STATE_WAITING_FOR_ADDR_1_ACK, + STATE_WAITING_FOR_ADDR_2_ACK, + STATE_WAITING_FOR_SLAVE_ACK, + STATE_WAITING_FOR_SLAVE_DATA, +}; + +/* Driver actions */ +enum mv64xxx_action { + ACTION_INVALID, + ACTION_CONTINUE, + ACTION_SEND_RESTART, + ACTION_OFFLOAD_RESTART, + ACTION_SEND_ADDR_1, + ACTION_SEND_ADDR_2, + ACTION_SEND_DATA, + ACTION_RCV_DATA, + ACTION_RCV_DATA_STOP, + ACTION_SEND_STOP, + ACTION_OFFLOAD_SEND_STOP, +}; + +struct mv64xxx_i2c_regs { + u8 addr; + u8 ext_addr; + u8 data; + u8 control; + u8 status; + u8 clock; + u8 soft_reset; +}; + +struct mv64xxx_i2c_data { + struct i2c_msg *msgs; + int num_msgs; + enum mv64xxx_state state; + enum mv64xxx_action action; + u8 cntl_bits; + void __iomem *reg_base; + struct mv64xxx_i2c_regs reg_offsets; + u8 addr1; + u8 addr2; + u8 bytes_left; + u8 byte_posn; + u8 send_stop; + bool block; + int rc; + u32 freq_m; + u32 freq_n; + struct clk *clk; + struct i2c_msg *msg; + struct i2c_adapter adapter; +/* 5us delay in order to avoid repeated start timing violation */ + bool errata_delay; + void (*write_reg)(struct mv64xxx_i2c_data *drv_data, u32 v, unsigned reg); + u32 (*read_reg)(struct mv64xxx_i2c_data *drv_data, unsigned reg); +}; + +static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = { + .addr = 0x00, + .ext_addr = 0x10, + .data = 0x04, + .control = 0x08, + .status = 0x0c, + .clock = 0x0c, + .soft_reset = 0x1c, +}; + +static void mv64xxx_writeb(struct mv64xxx_i2c_data *drv_data, + u32 v, unsigned reg) +{ + writeb(v, drv_data->reg_base + reg); +} + +static void mv64xxx_writel(struct mv64xxx_i2c_data *drv_data, + u32 v, unsigned reg) +{ + writel(v, drv_data->reg_base + reg); +} + +static inline void mv64xxx_write(struct mv64xxx_i2c_data *drv_data, + u32 v, unsigned reg) +{ + drv_data->write_reg(drv_data, v, reg); +} + +static u32 mv64xxx_readb(struct mv64xxx_i2c_data *drv_data, unsigned reg) +{ + return readb(drv_data->reg_base + reg); +} + +static u32 mv64xxx_readl(struct mv64xxx_i2c_data *drv_data, unsigned reg) +{ + return readl(drv_data->reg_base + reg); +} + +static inline u32 mv64xxx_read(struct mv64xxx_i2c_data *drv_data, unsigned reg) +{ + return drv_data->read_reg(drv_data, reg); +} + +static void +mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, + struct i2c_msg *msg) +{ + u32 dir = 0; + + drv_data->cntl_bits = REG_CONTROL_ACK | + REG_CONTROL_INTEN | REG_CONTROL_TWSIEN; + + if (msg->flags & I2C_M_RD) + dir = 1; + + if (msg->flags & I2C_M_TEN) { + drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir; + drv_data->addr2 = (u32)msg->addr & 0xff; + } else { + drv_data->addr1 = ADDR_ADDR((u32)msg->addr) | dir; + drv_data->addr2 = 0; + } +} + +/* + ***************************************************************************** + * + * Finite State Machine & Interrupt Routines + * + ***************************************************************************** + */ + +/* Reset hardware and initialize FSM */ +static void +mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data) +{ + mv64xxx_write(drv_data, 0, drv_data->reg_offsets.soft_reset); + mv64xxx_write(drv_data, BAUD_DIV_M(drv_data->freq_m) + | BAUD_DIV_N(drv_data->freq_n), + drv_data->reg_offsets.clock); + mv64xxx_write(drv_data, 0, drv_data->reg_offsets.addr); + mv64xxx_write(drv_data, 0, drv_data->reg_offsets.ext_addr); + mv64xxx_write(drv_data, REG_CONTROL_TWSIEN + | REG_CONTROL_STOP, + drv_data->reg_offsets.control); + drv_data->state = STATE_IDLE; +} + +static void +mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status) +{ + /* + * If state is idle, then this is likely the remnants of an old + * operation that driver has given up on or the user has killed. + * If so, issue the stop condition and go to idle. + */ + if (drv_data->state == STATE_IDLE) { + drv_data->action = ACTION_SEND_STOP; + return; + } + + /* The status from the ctlr [mostly] tells us what to do next */ + switch (status) { + /* Start condition interrupt */ + case STATUS_MAST_START: /* 0x08 */ + case STATUS_MAST_REPEAT_START: /* 0x10 */ + drv_data->action = ACTION_SEND_ADDR_1; + drv_data->state = STATE_WAITING_FOR_ADDR_1_ACK; + break; + + /* Performing a write */ + case STATUS_MAST_WR_ADDR_ACK: /* 0x18 */ + if (drv_data->msg->flags & I2C_M_TEN) { + drv_data->action = ACTION_SEND_ADDR_2; + drv_data->state = + STATE_WAITING_FOR_ADDR_2_ACK; + break; + } + /* FALLTHRU */ + case STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */ + case STATUS_MAST_WR_ACK: /* 0x28 */ + if (drv_data->bytes_left == 0) { + if (drv_data->send_stop) { + drv_data->action = ACTION_SEND_STOP; + drv_data->state = STATE_IDLE; + } else { + drv_data->action = + ACTION_SEND_RESTART; + drv_data->state = + STATE_WAITING_FOR_RESTART; + } + } else { + drv_data->action = ACTION_SEND_DATA; + drv_data->state = + STATE_WAITING_FOR_SLAVE_ACK; + drv_data->bytes_left--; + } + break; + + /* Performing a read */ + case STATUS_MAST_RD_ADDR_ACK: /* 40 */ + if (drv_data->msg->flags & I2C_M_TEN) { + drv_data->action = ACTION_SEND_ADDR_2; + drv_data->state = + STATE_WAITING_FOR_ADDR_2_ACK; + break; + } + /* FALLTHRU */ + case STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */ + if (drv_data->bytes_left == 0) { + drv_data->action = ACTION_SEND_STOP; + drv_data->state = STATE_IDLE; + break; + } + /* FALLTHRU */ + case STATUS_MAST_RD_DATA_ACK: /* 0x50 */ + if (status != STATUS_MAST_RD_DATA_ACK) + drv_data->action = ACTION_CONTINUE; + else { + drv_data->action = ACTION_RCV_DATA; + drv_data->bytes_left--; + } + drv_data->state = STATE_WAITING_FOR_SLAVE_DATA; + + if (drv_data->bytes_left == 1) + drv_data->cntl_bits &= ~REG_CONTROL_ACK; + break; + + case STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */ + drv_data->action = ACTION_RCV_DATA_STOP; + drv_data->state = STATE_IDLE; + break; + + case STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */ + case STATUS_MAST_WR_NO_ACK: /* 30 */ + case STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */ + /* Doesn't seem to be a device at other end */ + drv_data->action = ACTION_SEND_STOP; + drv_data->state = STATE_IDLE; + drv_data->rc = -ENXIO; + break; + + default: + dev_err(&drv_data->adapter.dev, + "mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, " + "status: 0x%x, addr: 0x%x, flags: 0x%x\n", + drv_data->state, status, drv_data->msg->addr, + drv_data->msg->flags); + drv_data->action = ACTION_SEND_STOP; + mv64xxx_i2c_hw_init(drv_data); + drv_data->rc = -EIO; + } +} + +static void mv64xxx_i2c_send_start(struct mv64xxx_i2c_data *drv_data) +{ + drv_data->msg = drv_data->msgs; + drv_data->byte_posn = 0; + drv_data->bytes_left = drv_data->msg->len; + drv_data->rc = 0; + + mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs); + mv64xxx_write(drv_data, drv_data->cntl_bits | REG_CONTROL_START, + drv_data->reg_offsets.control); +} + +static void +mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) +{ + switch (drv_data->action) { + case ACTION_SEND_RESTART: + /* We should only get here if we have further messages */ + BUG_ON(drv_data->num_msgs == 0); + + drv_data->msgs++; + drv_data->num_msgs--; + mv64xxx_i2c_send_start(drv_data); + + if (drv_data->errata_delay) + udelay(5); + + /* + * We're never at the start of the message here, and by this + * time it's already too late to do any protocol mangling. + * Thankfully, do not advertise support for that feature. + */ + drv_data->send_stop = drv_data->num_msgs == 1; + break; + + case ACTION_CONTINUE: + mv64xxx_write(drv_data, drv_data->cntl_bits, + drv_data->reg_offsets.control); + break; + + case ACTION_SEND_ADDR_1: + mv64xxx_write(drv_data, drv_data->addr1, + drv_data->reg_offsets.data); + mv64xxx_write(drv_data, drv_data->cntl_bits, + drv_data->reg_offsets.control); + break; + + case ACTION_SEND_ADDR_2: + mv64xxx_write(drv_data, drv_data->addr2, + drv_data->reg_offsets.data); + mv64xxx_write(drv_data, drv_data->cntl_bits, + drv_data->reg_offsets.control); + break; + + case ACTION_SEND_DATA: + mv64xxx_write(drv_data, drv_data->msg->buf[drv_data->byte_posn++], + drv_data->reg_offsets.data); + mv64xxx_write(drv_data, drv_data->cntl_bits, + drv_data->reg_offsets.control); + break; + + case ACTION_RCV_DATA: + drv_data->msg->buf[drv_data->byte_posn++] = + mv64xxx_read(drv_data, drv_data->reg_offsets.data); + mv64xxx_write(drv_data, drv_data->cntl_bits, + drv_data->reg_offsets.control); + break; + + case ACTION_RCV_DATA_STOP: + drv_data->msg->buf[drv_data->byte_posn++] = + mv64xxx_read(drv_data, drv_data->reg_offsets.data); + drv_data->cntl_bits &= ~REG_CONTROL_INTEN; + mv64xxx_write(drv_data, drv_data->cntl_bits | REG_CONTROL_STOP, + drv_data->reg_offsets.control); + drv_data->block = false; + if (drv_data->errata_delay) + udelay(5); + + break; + + case ACTION_INVALID: + default: + dev_err(&drv_data->adapter.dev, + "mv64xxx_i2c_do_action: Invalid action: %d\n", + drv_data->action); + drv_data->rc = -EIO; + + /* FALLTHRU */ + case ACTION_SEND_STOP: + drv_data->cntl_bits &= ~REG_CONTROL_INTEN; + mv64xxx_write(drv_data, drv_data->cntl_bits + | REG_CONTROL_STOP, + drv_data->reg_offsets.control); + drv_data->block = false; + break; + } +} + +static void mv64xxx_i2c_intr(struct mv64xxx_i2c_data *drv_data) +{ + u32 status; + uint64_t start; + + start = get_time_ns(); + + while (mv64xxx_read(drv_data, drv_data->reg_offsets.control) & + REG_CONTROL_IFLG) { + status = mv64xxx_read(drv_data, drv_data->reg_offsets.status); + mv64xxx_i2c_fsm(drv_data, status); + mv64xxx_i2c_do_action(drv_data); + + if (is_timeout_non_interruptible(start, 3 * SECOND)) { + drv_data->rc = -EIO; + break; + } + } +} + +/* + ***************************************************************************** + * + * I2C Msg Execution Routines + * + ***************************************************************************** + */ +static void +mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data) +{ + do { + mv64xxx_i2c_intr(drv_data); + if (drv_data->rc) { + drv_data->state = STATE_IDLE; + dev_err(&drv_data->adapter.dev, "I2C bus error\n"); + mv64xxx_i2c_hw_init(drv_data); + drv_data->block = false; + } + } while (drv_data->block); +} + +/* + ***************************************************************************** + * + * I2C Core Support Routines (Interface to higher level I2C code) + * + ***************************************************************************** + */ +static int +mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct mv64xxx_i2c_data *drv_data = container_of(adap, struct mv64xxx_i2c_data, adapter); + int ret = num; + + BUG_ON(drv_data->msgs != NULL); + + drv_data->msgs = msgs; + drv_data->num_msgs = num; + drv_data->state = STATE_WAITING_FOR_START_COND; + drv_data->send_stop = (num == 1); + drv_data->block = true; + mv64xxx_i2c_send_start(drv_data); + mv64xxx_i2c_wait_for_completion(drv_data); + + if (drv_data->rc < 0) + ret = drv_data->rc; + + drv_data->num_msgs = 0; + drv_data->msgs = NULL; + + return ret; +} + +/* + ***************************************************************************** + * + * Driver Interface & Early Init Routines + * + ***************************************************************************** + */ +static struct of_device_id mv64xxx_i2c_of_match_table[] = { + { .compatible = "marvell,mv64xxx-i2c", .data = (unsigned long)&mv64xxx_i2c_regs_mv64xxx}, + { .compatible = "marvell,mv78230-i2c", .data = (unsigned long)&mv64xxx_i2c_regs_mv64xxx}, + { .compatible = "marvell,mv78230-a0-i2c", .data = (unsigned long)&mv64xxx_i2c_regs_mv64xxx}, + {} +}; + +static inline int +mv64xxx_calc_freq(const int tclk, const int n, const int m) +{ + return tclk / (10 * (m + 1) * (2 << n)); +} + +static bool +mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n, + u32 *best_m) +{ + int freq, delta, best_delta = INT_MAX; + int m, n; + + for (n = 0; n <= 7; n++) + for (m = 0; m <= 15; m++) { + freq = mv64xxx_calc_freq(tclk, n, m); + delta = req_freq - freq; + if (delta >= 0 && delta < best_delta) { + *best_m = m; + *best_n = n; + best_delta = delta; + } + if (best_delta == 0) + return true; + } + if (best_delta == INT_MAX) + return false; + return true; +} + +static int +mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, + struct device_d *pd) +{ + struct device_node *np = pd->device_node; + u32 bus_freq, tclk; + int rc = 0; + u32 prop; + struct mv64xxx_i2c_regs *mv64xxx_regs; + int freq; + + if (IS_ERR(drv_data->clk)) { + rc = -ENODEV; + goto out; + } + tclk = clk_get_rate(drv_data->clk); + + rc = of_property_read_u32(np, "clock-frequency", &bus_freq); + if (rc) + bus_freq = 100000; /* 100kHz by default */ + + if (!mv64xxx_find_baud_factors(bus_freq, tclk, + &drv_data->freq_n, &drv_data->freq_m)) { + rc = -EINVAL; + goto out; + } + + freq = mv64xxx_calc_freq(tclk, drv_data->freq_n, drv_data->freq_m); + dev_dbg(pd, "tclk=%d freq_n=%d freq_m=%d freq=%d\n", + tclk, drv_data->freq_n, drv_data->freq_m, freq); + + if (of_property_read_u32(np, "reg-io-width", &prop)) { + /* Use 32-bit registers by default */ + prop = 4; + } + + switch (prop) { + case 1: + drv_data->write_reg = mv64xxx_writeb; + drv_data->read_reg = mv64xxx_readb; + break; + case 4: + drv_data->write_reg = mv64xxx_writel; + drv_data->read_reg = mv64xxx_readl; + break; + default: + dev_err(pd, "unsupported reg-io-width (%d)\n", prop); + rc = -EINVAL; + goto out; + } + + dev_get_drvdata(pd, (unsigned long *)&mv64xxx_regs); + memcpy(&drv_data->reg_offsets, mv64xxx_regs, + sizeof(drv_data->reg_offsets)); + + /* + * For controllers embedded in new SoCs activate the errata fix. + */ + if (of_device_is_compatible(np, "marvell,mv78230-i2c")) { + drv_data->errata_delay = true; + } + + if (of_device_is_compatible(np, "marvell,mv78230-a0-i2c")) { + drv_data->errata_delay = true; + } + +out: + return rc; +} + +static int +mv64xxx_i2c_probe(struct device_d *pd) +{ + struct mv64xxx_i2c_data *drv_data; + int rc; + + if (!pd->device_node) + return -ENODEV; + + drv_data = xzalloc(sizeof(*drv_data)); + + drv_data->reg_base = dev_request_mem_region(pd, 0); + if (IS_ERR(drv_data->reg_base)) + return PTR_ERR(drv_data->reg_base); + + drv_data->clk = clk_get(pd, NULL); + if (IS_ERR(drv_data->clk)) + return PTR_ERR(drv_data->clk); + + clk_enable(drv_data->clk); + + rc = mv64xxx_of_config(drv_data, pd); + if (rc) + goto exit_clk; + + drv_data->adapter.master_xfer = mv64xxx_i2c_xfer; + drv_data->adapter.dev.parent = pd; + drv_data->adapter.nr = pd->id; + drv_data->adapter.dev.device_node = pd->device_node; + + mv64xxx_i2c_hw_init(drv_data); + + rc = i2c_add_numbered_adapter(&drv_data->adapter); + if (rc) { + dev_err(pd, "Failed to add I2C adapter\n"); + goto exit_clk; + } + + return 0; + +exit_clk: + clk_disable(drv_data->clk); + + return rc; +} + +static struct driver_d mv64xxx_i2c_driver = { + .probe = mv64xxx_i2c_probe, + .name = "mv64xxx_i2c", + .of_compatible = DRV_OF_COMPAT(mv64xxx_i2c_of_match_table), +}; +device_platform_driver(mv64xxx_i2c_driver); diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c new file mode 100644 index 0000000..e8aeaa7 --- /dev/null +++ b/drivers/i2c/i2c-smbus.c @@ -0,0 +1,371 @@ +#include +#include +#include + +/* The SMBus parts */ + +#define POLY (0x1070U << 3) +static u8 crc8(u16 data) +{ + int i; + + for (i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +/* Incremental CRC8 over count bytes in the array pointed to by p */ +static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count) +{ + int i; + + for (i = 0; i < count; i++) + crc = crc8((crc ^ p[i]) << 8); + return crc; +} + +/* Assume a 7-bit address, which is reasonable for SMBus */ +static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg) +{ + /* The address will be sent first */ + u8 addr = (msg->addr << 1) | !!(msg->flags & I2C_M_RD); + pec = i2c_smbus_pec(pec, &addr, 1); + + /* The data buffer follows */ + return i2c_smbus_pec(pec, msg->buf, msg->len); +} + +/* Used for write only transactions */ +static inline void i2c_smbus_add_pec(struct i2c_msg *msg) +{ + msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg); + msg->len++; +} + +/* Return <0 on CRC error + If there was a write before this read (most cases) we need to take the + partial CRC from the write part into account. + Note that this function does modify the message (we need to decrease the + message length to hide the CRC byte from the caller). */ +static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg) +{ + u8 rpec = msg->buf[--msg->len]; + cpec = i2c_smbus_msg_pec(cpec, msg); + + if (rpec != cpec) { + pr_debug("i2c-core: Bad PEC 0x%02x vs. 0x%02x\n", + rpec, cpec); + return -EBADMSG; + } + return 0; +} + +/** + * i2c_smbus_read_byte - SMBus "receive byte" protocol + * @client: Handle to slave device + * + * This executes the SMBus "receive byte" protocol, returning negative errno + * else the byte received from the device. + */ +s32 i2c_smbus_read_byte(const struct i2c_client *client) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */, + I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte); + +/** + * i2c_smbus_write_byte - SMBus "send byte" protocol + * @client: Handle to slave device + * @value: Byte to be sent + * + * This executes the SMBus "send byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value) +{ + return i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */, + I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); +} +EXPORT_SYMBOL(i2c_smbus_write_byte); + +/** + * i2c_smbus_read_byte_data - SMBus "read byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read byte" protocol, returning negative errno + * else a data byte received from the device. + */ +s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte_data); + +/** + * i2c_smbus_write_byte_data - SMBus "write byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: Byte being written + * + * This executes the SMBus "write byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, + u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_byte_data); + +/** + * i2c_smbus_read_word_data - SMBus "read word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read word" protocol, returning negative errno + * else a 16-bit unsigned "word" received from the device. + */ +s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */, + I2C_SMBUS_READ, command, + I2C_SMBUS_WORD_DATA, &data); + return (status < 0) ? status : data.word; +} +EXPORT_SYMBOL(i2c_smbus_read_word_data); + +/** + * i2c_smbus_write_word_data - SMBus "write word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: 16-bit "word" being written + * + * This executes the SMBus "write word" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, + u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_WORD_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_word_data); + +/* Returns the number of read bytes */ +s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + union i2c_smbus_data data; + int status; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + status = i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */, + I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); + if (status < 0) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data); + +s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(data.block + 1, values, length); + return i2c_smbus_xfer(client->adapter, client->addr, 0 /* client->flags */, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data); + +/* Simulate a SMBus command using the i2c protocol + No checking of parameters is done! */ +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + /* So we need to generate a series of msgs. In the case of writing, we + need to use only one message; when reading, we need two. We initialize + most things with sane defaults, to keep the code below somewhat + simpler. */ + unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; + unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; + int num = read_write == I2C_SMBUS_READ ? 2 : 1; + int i; + u8 partial_pec = 0; + int status; + struct i2c_msg msg[2] = { + { + .addr = addr, + .flags = flags, + .len = 1, + .buf = msgbuf0, + }, { + .addr = addr, + .flags = flags | I2C_M_RD, + .len = 0, + .buf = msgbuf1, + }, + }; + + msgbuf0[0] = command; + switch (size) { + case I2C_SMBUS_QUICK: + msg[0].len = 0; + /* Special case: The read/write field is used as data */ + msg[0].flags = flags | (read_write == I2C_SMBUS_READ ? + I2C_M_RD : 0); + num = 1; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + /* Special case: only a read! */ + msg[0].flags = I2C_M_RD | flags; + num = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 1; + else { + msg[0].len = 2; + msgbuf0[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 2; + else { + msg[0].len = 3; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + } + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + msg[1].len = data->block[0]; + } else { + msg[0].len = data->block[0] + 1; + if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + for (i = 1; i <= data->block[0]; i++) + msgbuf0[i] = data->block[i]; + } + break; + default: + dev_err(&adapter->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK + && size != I2C_SMBUS_I2C_BLOCK_DATA); + if (i) { + /* Compute PEC if first message is a write */ + if (!(msg[0].flags & I2C_M_RD)) { + if (num == 1) /* Write only */ + i2c_smbus_add_pec(&msg[0]); + else /* Write followed by read */ + partial_pec = i2c_smbus_msg_pec(0, &msg[0]); + } + /* Ask for PEC if last message is a read */ + if (msg[num-1].flags & I2C_M_RD) + msg[num-1].len++; + } + + status = i2c_transfer(adapter, msg, num); + if (status < 0) + return status; + + /* Check PEC if last message is a read */ + if (i && (msg[num-1].flags & I2C_M_RD)) { + status = i2c_smbus_check_pec(partial_pec, &msg[num-1]); + if (status < 0) + return status; + } + + if (read_write == I2C_SMBUS_READ) + switch (size) { + case I2C_SMBUS_BYTE: + data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + for (i = 0; i < data->block[0]; i++) + data->block[i+1] = msgbuf1[i]; + break; + } + return 0; +} + +/** + * i2c_smbus_xfer - execute SMBus protocol operations + * @adapter: Handle to I2C bus + * @addr: Address of SMBus slave on that bus + * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC) + * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE + * @command: Byte interpreted by slave, for protocols which use such bytes + * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL + * @data: Data to be read or written + * + * This executes an SMBus protocol operation, and returns a negative + * errno code else zero on success. + */ +s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, + char read_write, u8 command, int protocol, + union i2c_smbus_data *data) +{ + s32 res; + + flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; + + res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, + command, protocol, data); + + return res; +} +EXPORT_SYMBOL(i2c_smbus_xfer); diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h index a107f5e..4696f43 100644 --- a/include/i2c/i2c.h +++ b/include/i2c/i2c.h @@ -85,6 +85,63 @@ #define to_i2c_client(a) container_of(a, struct i2c_client, dev) +/*flags for the client struct: */ +#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ +#define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */ + /* Must match I2C_M_STOP|IGNORE_NAK */ + +/* + * Data for SMBus Messages + */ +#define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ +union i2c_smbus_data { + __u8 byte; + __u16 word; + __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ + /* and one more for user-space compatibility */ +}; + +/* i2c_smbus_xfer read or write markers */ +#define I2C_SMBUS_READ 1 +#define I2C_SMBUS_WRITE 0 + +/* SMBus transaction types (size parameter in the above functions) + Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */ +#define I2C_SMBUS_QUICK 0 +#define I2C_SMBUS_BYTE 1 +#define I2C_SMBUS_BYTE_DATA 2 +#define I2C_SMBUS_WORD_DATA 3 +#define I2C_SMBUS_I2C_BLOCK_DATA 8 + +/* This is the very generalized SMBus access routine. You probably do not + want to use this, though; one of the functions below may be much easier, + and probably just as fast. + Note that we use i2c_adapter here, because you do not need a specific + smbus adapter to call this function. */ +extern s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data); + +/* Now follow the 'nice' access routines. These also document the calling + conventions of i2c_smbus_xfer. */ + +extern s32 i2c_smbus_read_byte(const struct i2c_client *client); +extern s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value); +extern s32 i2c_smbus_read_byte_data(const struct i2c_client *client, + u8 command); +extern s32 i2c_smbus_write_byte_data(const struct i2c_client *client, + u8 command, u8 value); +extern s32 i2c_smbus_read_word_data(const struct i2c_client *client, + u8 command); +extern s32 i2c_smbus_write_word_data(const struct i2c_client *client, + u8 command, u16 value); + +/* Returns the number of read bytes */ +extern s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, + u8 command, u8 length, u8 *values); +extern s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, + u8 command, u8 length, + const u8 *values); /** * struct i2c_board_info - template for device creation diff --git a/include/platform_data/pca953x.h b/include/platform_data/pca953x.h new file mode 100644 index 0000000..cfd253e --- /dev/null +++ b/include/platform_data/pca953x.h @@ -0,0 +1,27 @@ +#ifndef _LINUX_PCA953X_H +#define _LINUX_PCA953X_H + +#include +#include + +/* platform data for the PCA9539 16-bit I/O expander driver */ + +struct pca953x_platform_data { + /* number of the first GPIO */ + unsigned gpio_base; + + /* initial polarity inversion setting */ + u32 invert; + + void *context; /* param to setup/teardown */ + + int (*setup)(struct i2c_client *client, + unsigned gpio, unsigned ngpio, + void *context); + int (*teardown)(struct i2c_client *client, + unsigned gpio, unsigned ngpio, + void *context); + const char *const *names; +}; + +#endif /* _LINUX_PCA953X_H */