Merge branch 'i2c-embedded/for-next' of git://git.pengutronix.de/git/wsa/linux
Pull i2c-embedded changes from Wolfram Sang: "The changes for i2c-embedded include: - massive rework of the omap driver - massive rework of the at91 driver. In fact, the old driver gets removed; I am okay with this approach since the old driver was depending on BROKEN and its limitations made it practically unusable, so people used bitbanging instead. But even if there are users, there is no platform_data or module parameter which would need to be converted. It is just another driver doing I2C transfers, just way better. Modifications of arch/arm/at91 related files have proper acks from the maintainer. - new driver for R-Car I2C - devicetree and generic_clock conversions and fixes - usual driver fixes and changes. The rework patches have come a long way and lots of people have been involved in creating/testing them. Most patches have been in linux-next at least since 3.6-rc5. A few have been added in the last week, I have to admit. An unexpected (but welcome :)) peak in private life is the cause for that. The "late" patches shouldn't cause any merge conflicts and I will have a special eye on them during the stabilization phase. This is an exception and I want to have the patches in place properly in time again for the next kernels." * 'i2c-embedded/for-next' of git://git.pengutronix.de/git/wsa/linux: (44 commits) MXS: Implement DMA support into mxs-i2c i2c: add Renesas R-Car I2C driver i2c: s3c2410: use clk_prepare_enable and clk_disable_unprepare ARM: OMAP: convert I2C driver to PM QoS for MPU latency constraints i2c: nomadik: Add Device Tree support to the Nomadik I2C driver i2c: algo: pca: Fix chip reset function for PCA9665 i2c: mpc: Wait for STOP to hit the bus i2c: davinci: preparation for switch to common clock framework omap-i2c: fix incorrect log message when using a device tree i2c: omap: sanitize exit path i2c: omap: switch over to autosuspend API i2c: omap: remove unnecessary pm_runtime_suspended check i2c: omap: switch to threaded IRQ support i2c: omap: remove redundant status read i2c: omap: get rid of the "complete" label i2c: omap: resize fifos before each message i2c: omap: simplify IRQ exit path i2c: omap: always return IRQ_HANDLED i2c: omap: simplify errata check i2c: omap: bus: add a receiver flag ...
This commit is contained in:
Коммит
12250d843e
|
@ -0,0 +1,30 @@
|
|||
I2C for Atmel platforms
|
||||
|
||||
Required properties :
|
||||
- compatible : Must be "atmel,at91rm9200-i2c", "atmel,at91sam9261-i2c",
|
||||
"atmel,at91sam9260-i2c", "atmel,at91sam9g20-i2c", "atmel,at91sam9g10-i2c"
|
||||
or "atmel,at91sam9x5-i2c"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt number to the cpu.
|
||||
- #address-cells = <1>;
|
||||
- #size-cells = <0>;
|
||||
|
||||
Optional properties:
|
||||
- Child nodes conforming to i2c bus binding
|
||||
|
||||
Examples :
|
||||
|
||||
i2c0: i2c@fff84000 {
|
||||
compatible = "atmel,at91sam9g20-i2c";
|
||||
reg = <0xfff84000 0x100>;
|
||||
interrupts = <12 4 6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
24c512@50 {
|
||||
compatible = "24c512";
|
||||
reg = <0x50>;
|
||||
pagesize = <128>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
* Texas Instruments Davinci I2C
|
||||
|
||||
This file provides information, what the device node for the
|
||||
davinci i2c interface contain.
|
||||
|
||||
Required properties:
|
||||
- compatible: "ti,davinci-i2c";
|
||||
- reg : Offset and length of the register set for the device
|
||||
|
||||
Recommended properties :
|
||||
- interrupts : standard interrupt property.
|
||||
- clock-frequency : desired I2C bus clock frequency in Hz.
|
||||
|
||||
Example (enbw_cmc board):
|
||||
i2c@1c22000 {
|
||||
compatible = "ti,davinci-i2c";
|
||||
reg = <0x22000 0x1000>;
|
||||
clock-frequency = <100000>;
|
||||
interrupts = <15>;
|
||||
interrupt-parent = <&intc>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
dtt@48 {
|
||||
compatible = "national,lm75";
|
||||
reg = <0x48>;
|
||||
};
|
||||
};
|
|
@ -6,6 +6,7 @@ Required properties:
|
|||
- interrupts: Should contain ERROR and DMA interrupts
|
||||
- clock-frequency: Desired I2C bus clock frequency in Hz.
|
||||
Only 100000Hz and 400000Hz modes are supported.
|
||||
- fsl,i2c-dma-channel: APBX DMA channel for the I2C
|
||||
|
||||
Examples:
|
||||
|
||||
|
@ -16,4 +17,5 @@ i2c0: i2c@80058000 {
|
|||
reg = <0x80058000 2000>;
|
||||
interrupts = <111 68>;
|
||||
clock-frequency = <100000>;
|
||||
fsl,i2c-dma-channel = <6>;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
I2C for Nomadik based systems
|
||||
|
||||
Required (non-standard) properties:
|
||||
- Nil
|
||||
|
||||
Recommended (non-standard) properties:
|
||||
- clock-frequency : Maximum bus clock frequency for the device
|
||||
|
||||
Optional (non-standard) properties:
|
||||
- Nil
|
||||
|
||||
Example :
|
||||
|
||||
i2c@80004000 {
|
||||
compatible = "stericsson,db8500-i2c", "st,nomadik-i2c";
|
||||
reg = <0x80004000 0x1000>;
|
||||
interrupts = <0 21 0x4>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
v-i2c-supply = <&db8500_vape_reg>;
|
||||
|
||||
clock-frequency = <400000>;
|
||||
};
|
|
@ -28,6 +28,7 @@
|
|||
gpio2 = &pioC;
|
||||
tcb0 = &tcb0;
|
||||
tcb1 = &tcb1;
|
||||
i2c0 = &i2c0;
|
||||
};
|
||||
cpus {
|
||||
cpu@0 {
|
||||
|
@ -202,6 +203,15 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c0: i2c@fffac000 {
|
||||
compatible = "atmel,at91sam9260-i2c";
|
||||
reg = <0xfffac000 0x100>;
|
||||
interrupts = <11 4 6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
adc0: adc@fffe0000 {
|
||||
compatible = "atmel,at91sam9260-adc";
|
||||
reg = <0xfffe0000 0x100>;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
gpio3 = &pioD;
|
||||
gpio4 = &pioE;
|
||||
tcb0 = &tcb0;
|
||||
i2c0 = &i2c0;
|
||||
};
|
||||
cpus {
|
||||
cpu@0 {
|
||||
|
@ -185,6 +186,15 @@
|
|||
interrupts = <24 4 2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c0: i2c@fff88000 {
|
||||
compatible = "atmel,at91sam9263-i2c";
|
||||
reg = <0xfff88000 0x100>;
|
||||
interrupts = <13 4 6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
|
||||
ahb {
|
||||
apb {
|
||||
i2c0: i2c@fffac000 {
|
||||
compatible = "atmel,at91sam9g20-i2c";
|
||||
};
|
||||
|
||||
adc0: adc@fffe0000 {
|
||||
atmel,adc-startup-time = <40>;
|
||||
};
|
||||
|
|
|
@ -32,6 +32,18 @@
|
|||
phy-mode = "rmii";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
i2c0: i2c@f8010000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
i2c1: i2c@f8014000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
i2c2: i2c@f8018000 {
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
usb0: ohci@00600000 {
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
gpio4 = &pioE;
|
||||
tcb0 = &tcb0;
|
||||
tcb1 = &tcb1;
|
||||
i2c0 = &i2c0;
|
||||
i2c1 = &i2c1;
|
||||
};
|
||||
cpus {
|
||||
cpu@0 {
|
||||
|
@ -206,6 +208,24 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c0: i2c@fff84000 {
|
||||
compatible = "atmel,at91sam9g10-i2c";
|
||||
reg = <0xfff84000 0x100>;
|
||||
interrupts = <12 4 6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c1: i2c@fff88000 {
|
||||
compatible = "atmel,at91sam9g10-i2c";
|
||||
reg = <0xfff88000 0x100>;
|
||||
interrupts = <13 4 6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
adc0: adc@fffb0000 {
|
||||
compatible = "atmel,at91sam9260-adc";
|
||||
reg = <0xfffb0000 0x100>;
|
||||
|
|
|
@ -46,6 +46,14 @@
|
|||
phy-mode = "rmii";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
i2c0: i2c@fff84000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
i2c1: i2c@fff88000 {
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
gpio3 = &pioD;
|
||||
tcb0 = &tcb0;
|
||||
tcb1 = &tcb1;
|
||||
i2c0 = &i2c0;
|
||||
i2c1 = &i2c1;
|
||||
};
|
||||
cpus {
|
||||
cpu@0 {
|
||||
|
@ -182,6 +184,24 @@
|
|||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c0: i2c@f8010000 {
|
||||
compatible = "atmel,at91sam9x5-i2c";
|
||||
reg = <0xf8010000 0x100>;
|
||||
interrupts = <9 4 6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c1: i2c@f8014000 {
|
||||
compatible = "atmel,at91sam9x5-i2c";
|
||||
reg = <0xf8014000 0x100>;
|
||||
interrupts = <10 4 6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
|
|
@ -37,6 +37,14 @@
|
|||
dbgu: serial@fffff200 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
i2c0: i2c@f8010000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
i2c1: i2c@f8014000 {
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
gpio3 = &pioD;
|
||||
tcb0 = &tcb0;
|
||||
tcb1 = &tcb1;
|
||||
i2c0 = &i2c0;
|
||||
i2c1 = &i2c1;
|
||||
i2c2 = &i2c2;
|
||||
};
|
||||
cpus {
|
||||
cpu@0 {
|
||||
|
@ -196,6 +199,33 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c0: i2c@f8010000 {
|
||||
compatible = "atmel,at91sam9x5-i2c";
|
||||
reg = <0xf8010000 0x100>;
|
||||
interrupts = <9 4 6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c1: i2c@f8014000 {
|
||||
compatible = "atmel,at91sam9x5-i2c";
|
||||
reg = <0xf8014000 0x100>;
|
||||
interrupts = <10 4 6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c2: i2c@f8018000 {
|
||||
compatible = "atmel,at91sam9x5-i2c";
|
||||
reg = <0xf8018000 0x100>;
|
||||
interrupts = <11 4 6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
adc0: adc@f804c000 {
|
||||
compatible = "atmel,at91sam9260-adc";
|
||||
reg = <0xf804c000 0x100>;
|
||||
|
|
|
@ -764,6 +764,7 @@
|
|||
reg = <0x80058000 0x2000>;
|
||||
interrupts = <111 68>;
|
||||
clock-frequency = <100000>;
|
||||
fsl,i2c-dma-channel = <6>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -774,6 +775,7 @@
|
|||
reg = <0x8005a000 0x2000>;
|
||||
interrupts = <110 69>;
|
||||
clock-frequency = <100000>;
|
||||
fsl,i2c-dma-channel = <7>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
|
|
@ -187,6 +187,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200", &twi_clk),
|
||||
/* fake hclk clock */
|
||||
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
|
||||
CLKDEV_CON_ID("pioA", &pioA_clk),
|
||||
|
|
|
@ -511,7 +511,7 @@ static struct resource twi_resources[] = {
|
|||
};
|
||||
|
||||
static struct platform_device at91rm9200_twi_device = {
|
||||
.name = "at91_i2c",
|
||||
.name = "i2c-at91rm9200",
|
||||
.id = -1,
|
||||
.resource = twi_resources,
|
||||
.num_resources = ARRAY_SIZE(twi_resources),
|
||||
|
|
|
@ -211,6 +211,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk),
|
||||
CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260", &twi_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20", &twi_clk),
|
||||
/* more usart lookup table for DT entries */
|
||||
CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck),
|
||||
CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk),
|
||||
|
@ -219,6 +221,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("usart", "fffd0000.serial", &usart3_clk),
|
||||
CLKDEV_CON_DEV_ID("usart", "fffd4000.serial", &usart4_clk),
|
||||
CLKDEV_CON_DEV_ID("usart", "fffd8000.serial", &usart5_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffac000.i2c", &twi_clk),
|
||||
/* more tc lookup table for DT entries */
|
||||
CLKDEV_CON_DEV_ID("t0_clk", "fffa0000.timer", &tc0_clk),
|
||||
CLKDEV_CON_DEV_ID("t1_clk", "fffa0000.timer", &tc1_clk),
|
||||
|
|
|
@ -421,7 +421,6 @@ static struct resource twi_resources[] = {
|
|||
};
|
||||
|
||||
static struct platform_device at91sam9260_twi_device = {
|
||||
.name = "at91_i2c",
|
||||
.id = -1,
|
||||
.resource = twi_resources,
|
||||
.num_resources = ARRAY_SIZE(twi_resources),
|
||||
|
@ -429,6 +428,13 @@ static struct platform_device at91sam9260_twi_device = {
|
|||
|
||||
void __init at91_add_device_i2c(struct i2c_board_info *devices, int nr_devices)
|
||||
{
|
||||
/* IP version is not the same on 9260 and g20 */
|
||||
if (cpu_is_at91sam9g20()) {
|
||||
at91sam9260_twi_device.name = "i2c-at91sam9g20";
|
||||
} else {
|
||||
at91sam9260_twi_device.name = "i2c-at91sam9260";
|
||||
}
|
||||
|
||||
/* pins used for TWI interface */
|
||||
at91_set_A_periph(AT91_PIN_PA23, 0); /* TWD */
|
||||
at91_set_multi_drive(AT91_PIN_PA23, 1);
|
||||
|
|
|
@ -178,6 +178,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk),
|
||||
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261", &twi_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10", &twi_clk),
|
||||
CLKDEV_CON_ID("pioA", &pioA_clk),
|
||||
CLKDEV_CON_ID("pioB", &pioB_clk),
|
||||
CLKDEV_CON_ID("pioC", &pioC_clk),
|
||||
|
|
|
@ -317,7 +317,6 @@ static struct resource twi_resources[] = {
|
|||
};
|
||||
|
||||
static struct platform_device at91sam9261_twi_device = {
|
||||
.name = "at91_i2c",
|
||||
.id = -1,
|
||||
.resource = twi_resources,
|
||||
.num_resources = ARRAY_SIZE(twi_resources),
|
||||
|
@ -325,12 +324,19 @@ static struct platform_device at91sam9261_twi_device = {
|
|||
|
||||
void __init at91_add_device_i2c(struct i2c_board_info *devices, int nr_devices)
|
||||
{
|
||||
/* IP version is not the same on 9261 and g10 */
|
||||
if (cpu_is_at91sam9g10()) {
|
||||
at91sam9261_twi_device.name = "i2c-at91sam9g10";
|
||||
/* I2C PIO must not be configured as open-drain on this chip */
|
||||
} else {
|
||||
at91sam9261_twi_device.name = "i2c-at91sam9261";
|
||||
at91_set_multi_drive(AT91_PIN_PA7, 1);
|
||||
at91_set_multi_drive(AT91_PIN_PA8, 1);
|
||||
}
|
||||
|
||||
/* pins used for TWI interface */
|
||||
at91_set_A_periph(AT91_PIN_PA7, 0); /* TWD */
|
||||
at91_set_multi_drive(AT91_PIN_PA7, 1);
|
||||
|
||||
at91_set_A_periph(AT91_PIN_PA8, 0); /* TWCK */
|
||||
at91_set_multi_drive(AT91_PIN_PA8, 1);
|
||||
|
||||
i2c_register_board_info(0, devices, nr_devices);
|
||||
platform_device_register(&at91sam9261_twi_device);
|
||||
|
|
|
@ -193,6 +193,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk),
|
||||
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tcb_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260", &twi_clk),
|
||||
/* fake hclk clock */
|
||||
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
|
||||
CLKDEV_CON_ID("pioA", &pioA_clk),
|
||||
|
@ -210,6 +211,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("hclk", "a00000.ohci", &ohci_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "fffa4000.spi", &spi0_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "fffa8000.spi", &spi1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fff88000.i2c", &twi_clk),
|
||||
};
|
||||
|
||||
static struct clk_lookup usart_clocks_lookups[] = {
|
||||
|
|
|
@ -599,7 +599,7 @@ static struct resource twi_resources[] = {
|
|||
};
|
||||
|
||||
static struct platform_device at91sam9263_twi_device = {
|
||||
.name = "at91_i2c",
|
||||
.name = "i2c-at91sam9260",
|
||||
.id = -1,
|
||||
.resource = twi_resources,
|
||||
.num_resources = ARRAY_SIZE(twi_resources),
|
||||
|
|
|
@ -237,6 +237,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk),
|
||||
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tcb0_clk),
|
||||
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tcb0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk),
|
||||
|
@ -254,6 +256,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("t0_clk", "fffd4000.timer", &tcb0_clk),
|
||||
CLKDEV_CON_DEV_ID("hclk", "700000.ohci", &uhphs_clk),
|
||||
CLKDEV_CON_DEV_ID("ehci_clk", "800000.ehci", &uhphs_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fff84000.i2c", &twi0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fff88000.i2c", &twi1_clk),
|
||||
/* fake hclk clock */
|
||||
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &uhphs_clk),
|
||||
CLKDEV_CON_ID("pioA", &pioA_clk),
|
||||
|
|
|
@ -653,7 +653,7 @@ static struct resource twi0_resources[] = {
|
|||
};
|
||||
|
||||
static struct platform_device at91sam9g45_twi0_device = {
|
||||
.name = "at91_i2c",
|
||||
.name = "i2c-at91sam9g10",
|
||||
.id = 0,
|
||||
.resource = twi0_resources,
|
||||
.num_resources = ARRAY_SIZE(twi0_resources),
|
||||
|
@ -673,7 +673,7 @@ static struct resource twi1_resources[] = {
|
|||
};
|
||||
|
||||
static struct platform_device at91sam9g45_twi1_device = {
|
||||
.name = "at91_i2c",
|
||||
.name = "i2c-at91sam9g10",
|
||||
.id = 1,
|
||||
.resource = twi1_resources,
|
||||
.num_resources = ARRAY_SIZE(twi1_resources),
|
||||
|
@ -686,18 +686,12 @@ void __init at91_add_device_i2c(short i2c_id, struct i2c_board_info *devices, in
|
|||
/* pins used for TWI interface */
|
||||
if (i2c_id == 0) {
|
||||
at91_set_A_periph(AT91_PIN_PA20, 0); /* TWD */
|
||||
at91_set_multi_drive(AT91_PIN_PA20, 1);
|
||||
|
||||
at91_set_A_periph(AT91_PIN_PA21, 0); /* TWCK */
|
||||
at91_set_multi_drive(AT91_PIN_PA21, 1);
|
||||
|
||||
platform_device_register(&at91sam9g45_twi0_device);
|
||||
} else {
|
||||
at91_set_A_periph(AT91_PIN_PB10, 0); /* TWD */
|
||||
at91_set_multi_drive(AT91_PIN_PB10, 1);
|
||||
|
||||
at91_set_A_periph(AT91_PIN_PB11, 0); /* TWCK */
|
||||
at91_set_multi_drive(AT91_PIN_PB11, 1);
|
||||
|
||||
platform_device_register(&at91sam9g45_twi1_device);
|
||||
}
|
||||
|
|
|
@ -169,6 +169,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("t0_clk", "f8008000.timer", &tcb_clk),
|
||||
CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb_clk),
|
||||
CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
|
||||
CLKDEV_CON_ID("pioA", &pioAB_clk),
|
||||
CLKDEV_CON_ID("pioB", &pioAB_clk),
|
||||
CLKDEV_CON_ID("pioC", &pioCD_clk),
|
||||
|
|
|
@ -186,6 +186,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk),
|
||||
CLKDEV_CON_ID("pioA", &pioA_clk),
|
||||
CLKDEV_CON_ID("pioB", &pioB_clk),
|
||||
CLKDEV_CON_ID("pioC", &pioC_clk),
|
||||
|
|
|
@ -346,7 +346,7 @@ static struct resource twi_resources[] = {
|
|||
};
|
||||
|
||||
static struct platform_device at91sam9rl_twi_device = {
|
||||
.name = "at91_i2c",
|
||||
.name = "i2c-at91sam9g20",
|
||||
.id = -1,
|
||||
.resource = twi_resources,
|
||||
.num_resources = ARRAY_SIZE(twi_resources),
|
||||
|
|
|
@ -231,6 +231,9 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk),
|
||||
CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk),
|
||||
CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk),
|
||||
CLKDEV_CON_ID("pioA", &pioAB_clk),
|
||||
CLKDEV_CON_ID("pioB", &pioAB_clk),
|
||||
CLKDEV_CON_ID("pioC", &pioCD_clk),
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* arch/arm/mach-at91/include/mach/at91_twi.h
|
||||
*
|
||||
* Copyright (C) 2005 Ivan Kokshaysky
|
||||
* Copyright (C) SAN People
|
||||
*
|
||||
* Two-wire Interface (TWI) registers.
|
||||
* Based on AT91RM9200 datasheet revision E.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef AT91_TWI_H
|
||||
#define AT91_TWI_H
|
||||
|
||||
#define AT91_TWI_CR 0x00 /* Control Register */
|
||||
#define AT91_TWI_START (1 << 0) /* Send a Start Condition */
|
||||
#define AT91_TWI_STOP (1 << 1) /* Send a Stop Condition */
|
||||
#define AT91_TWI_MSEN (1 << 2) /* Master Transfer Enable */
|
||||
#define AT91_TWI_MSDIS (1 << 3) /* Master Transfer Disable */
|
||||
#define AT91_TWI_SVEN (1 << 4) /* Slave Transfer Enable [SAM9260 only] */
|
||||
#define AT91_TWI_SVDIS (1 << 5) /* Slave Transfer Disable [SAM9260 only] */
|
||||
#define AT91_TWI_SWRST (1 << 7) /* Software Reset */
|
||||
|
||||
#define AT91_TWI_MMR 0x04 /* Master Mode Register */
|
||||
#define AT91_TWI_IADRSZ (3 << 8) /* Internal Device Address Size */
|
||||
#define AT91_TWI_IADRSZ_NO (0 << 8)
|
||||
#define AT91_TWI_IADRSZ_1 (1 << 8)
|
||||
#define AT91_TWI_IADRSZ_2 (2 << 8)
|
||||
#define AT91_TWI_IADRSZ_3 (3 << 8)
|
||||
#define AT91_TWI_MREAD (1 << 12) /* Master Read Direction */
|
||||
#define AT91_TWI_DADR (0x7f << 16) /* Device Address */
|
||||
|
||||
#define AT91_TWI_SMR 0x08 /* Slave Mode Register [SAM9260 only] */
|
||||
#define AT91_TWI_SADR (0x7f << 16) /* Slave Address */
|
||||
|
||||
#define AT91_TWI_IADR 0x0c /* Internal Address Register */
|
||||
|
||||
#define AT91_TWI_CWGR 0x10 /* Clock Waveform Generator Register */
|
||||
#define AT91_TWI_CLDIV (0xff << 0) /* Clock Low Divisor */
|
||||
#define AT91_TWI_CHDIV (0xff << 8) /* Clock High Divisor */
|
||||
#define AT91_TWI_CKDIV (7 << 16) /* Clock Divider */
|
||||
|
||||
#define AT91_TWI_SR 0x20 /* Status Register */
|
||||
#define AT91_TWI_TXCOMP (1 << 0) /* Transmission Complete */
|
||||
#define AT91_TWI_RXRDY (1 << 1) /* Receive Holding Register Ready */
|
||||
#define AT91_TWI_TXRDY (1 << 2) /* Transmit Holding Register Ready */
|
||||
#define AT91_TWI_SVREAD (1 << 3) /* Slave Read [SAM9260 only] */
|
||||
#define AT91_TWI_SVACC (1 << 4) /* Slave Access [SAM9260 only] */
|
||||
#define AT91_TWI_GACC (1 << 5) /* General Call Access [SAM9260 only] */
|
||||
#define AT91_TWI_OVRE (1 << 6) /* Overrun Error [AT91RM9200 only] */
|
||||
#define AT91_TWI_UNRE (1 << 7) /* Underrun Error [AT91RM9200 only] */
|
||||
#define AT91_TWI_NACK (1 << 8) /* Not Acknowledged */
|
||||
#define AT91_TWI_ARBLST (1 << 9) /* Arbitration Lost [SAM9260 only] */
|
||||
#define AT91_TWI_SCLWS (1 << 10) /* Clock Wait State [SAM9260 only] */
|
||||
#define AT91_TWI_EOSACC (1 << 11) /* End of Slave Address [SAM9260 only] */
|
||||
|
||||
#define AT91_TWI_IER 0x24 /* Interrupt Enable Register */
|
||||
#define AT91_TWI_IDR 0x28 /* Interrupt Disable Register */
|
||||
#define AT91_TWI_IMR 0x2c /* Interrupt Mask Register */
|
||||
#define AT91_TWI_RHR 0x30 /* Receive Holding Register */
|
||||
#define AT91_TWI_THR 0x34 /* Transmit Holding Register */
|
||||
|
||||
#endif
|
||||
|
|
@ -26,14 +26,12 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-omap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <mach/irqs.h>
|
||||
#include <plat/i2c.h>
|
||||
#include <plat/omap-pm.h>
|
||||
#include <plat/omap_device.h>
|
||||
|
||||
#define OMAP_I2C_SIZE 0x3f
|
||||
|
@ -129,16 +127,6 @@ static inline int omap1_i2c_add_bus(int bus_id)
|
|||
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP2PLUS
|
||||
/*
|
||||
* XXX This function is a temporary compatibility wrapper - only
|
||||
* needed until the I2C driver can be converted to call
|
||||
* omap_pm_set_max_dev_wakeup_lat() and handle a return code.
|
||||
*/
|
||||
static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
|
||||
{
|
||||
omap_pm_set_max_mpu_wakeup_lat(dev, t);
|
||||
}
|
||||
|
||||
static inline int omap2_i2c_add_bus(int bus_id)
|
||||
{
|
||||
int l;
|
||||
|
@ -170,15 +158,6 @@ static inline int omap2_i2c_add_bus(int bus_id)
|
|||
dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
|
||||
pdata->flags = dev_attr->flags;
|
||||
|
||||
/*
|
||||
* When waiting for completion of a i2c transfer, we need to
|
||||
* set a wake up latency constraint for the MPU. This is to
|
||||
* ensure quick enough wakeup from idle, when transfer
|
||||
* completes.
|
||||
* Only omap3 has support for constraints
|
||||
*/
|
||||
if (cpu_is_omap34xx())
|
||||
pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
|
||||
pdev = omap_device_build(name, bus_id, oh, pdata,
|
||||
sizeof(struct omap_i2c_bus_platform_data),
|
||||
NULL, 0, 0);
|
||||
|
|
|
@ -46,14 +46,19 @@ static int i2c_debug;
|
|||
#define pca_set_con(adap, val) pca_outw(adap, I2C_PCA_CON, val)
|
||||
#define pca_get_con(adap) pca_inw(adap, I2C_PCA_CON)
|
||||
#define pca_wait(adap) adap->wait_for_completion(adap->data)
|
||||
#define pca_reset(adap) adap->reset_chip(adap->data)
|
||||
|
||||
static void pca9665_reset(void *pd)
|
||||
static void pca_reset(struct i2c_algo_pca_data *adap)
|
||||
{
|
||||
struct i2c_algo_pca_data *adap = pd;
|
||||
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IPRESET);
|
||||
pca_outw(adap, I2C_PCA_IND, 0xA5);
|
||||
pca_outw(adap, I2C_PCA_IND, 0x5A);
|
||||
if (adap->chip == I2C_PCA_CHIP_9665) {
|
||||
/* Ignore the reset function from the module,
|
||||
* we can use the parallel bus reset.
|
||||
*/
|
||||
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IPRESET);
|
||||
pca_outw(adap, I2C_PCA_IND, 0xA5);
|
||||
pca_outw(adap, I2C_PCA_IND, 0x5A);
|
||||
} else {
|
||||
adap->reset_chip(adap->data);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -378,11 +383,12 @@ static unsigned int pca_probe_chip(struct i2c_adapter *adap)
|
|||
pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IADR);
|
||||
if (pca_inw(pca_data, I2C_PCA_IND) == 0xAA) {
|
||||
printk(KERN_INFO "%s: PCA9665 detected.\n", adap->name);
|
||||
return I2C_PCA_CHIP_9665;
|
||||
pca_data->chip = I2C_PCA_CHIP_9665;
|
||||
} else {
|
||||
printk(KERN_INFO "%s: PCA9564 detected.\n", adap->name);
|
||||
return I2C_PCA_CHIP_9564;
|
||||
pca_data->chip = I2C_PCA_CHIP_9564;
|
||||
}
|
||||
return pca_data->chip;
|
||||
}
|
||||
|
||||
static int pca_init(struct i2c_adapter *adap)
|
||||
|
@ -456,11 +462,6 @@ static int pca_init(struct i2c_adapter *adap)
|
|||
*/
|
||||
int raise_fall_time;
|
||||
|
||||
/* Ignore the reset function from the module,
|
||||
* we can use the parallel bus reset
|
||||
*/
|
||||
pca_data->reset_chip = pca9665_reset;
|
||||
|
||||
if (pca_data->i2c_clock > 1265800) {
|
||||
printk(KERN_WARNING "%s: I2C clock speed too high."
|
||||
" Using 1265.8kHz.\n", adap->name);
|
||||
|
|
|
@ -294,18 +294,21 @@ comment "I2C system bus drivers (mostly embedded / system-on-chip)"
|
|||
|
||||
config I2C_AT91
|
||||
tristate "Atmel AT91 I2C Two-Wire interface (TWI)"
|
||||
depends on ARCH_AT91 && EXPERIMENTAL && BROKEN
|
||||
depends on ARCH_AT91 && EXPERIMENTAL
|
||||
help
|
||||
This supports the use of the I2C interface on Atmel AT91
|
||||
processors.
|
||||
|
||||
This driver is BROKEN because the controller which it uses
|
||||
will easily trigger RX overrun and TX underrun errors. Using
|
||||
low I2C clock rates may partially work around those issues
|
||||
on some systems. Another serious problem is that there is no
|
||||
documented way to issue repeated START conditions, as needed
|
||||
A serious problem is that there is no documented way to issue
|
||||
repeated START conditions for more than two messages, as needed
|
||||
to support combined I2C messages. Use the i2c-gpio driver
|
||||
unless your system can cope with those limitations.
|
||||
unless your system can cope with this limitation.
|
||||
|
||||
Caution! at91rm9200, at91sam9261, at91sam9260, at91sam9263 devices
|
||||
don't have clock stretching in transmission mode. For that reason,
|
||||
you can encounter underrun issues causing premature stop sendings if
|
||||
the latency to fill the transmission register is too long. If you
|
||||
are facing this situation, use the i2c-gpio driver.
|
||||
|
||||
config I2C_AU1550
|
||||
tristate "Au1550/Au1200/Au1300 SMBus interface"
|
||||
|
@ -718,6 +721,16 @@ config I2C_XLR
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-xlr.
|
||||
|
||||
config I2C_RCAR
|
||||
tristate "Renesas R-Car I2C Controller"
|
||||
depends on ARCH_SHMOBILE && I2C
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
R-Car I2C controller.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-rcar.
|
||||
|
||||
comment "External I2C/SMBus adapter drivers"
|
||||
|
||||
config I2C_DIOLAN_U2C
|
||||
|
|
|
@ -71,6 +71,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
|
|||
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
|
||||
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
|
||||
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
|
||||
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
|
||||
|
||||
# External I2C/SMBus adapter drivers
|
||||
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
|
||||
|
|
|
@ -1,315 +1,554 @@
|
|||
/*
|
||||
i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
|
||||
|
||||
Copyright (C) 2004 Rick Bronson
|
||||
Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
|
||||
|
||||
Borrowed heavily from original work by:
|
||||
Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
|
||||
|
||||
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 <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <mach/at91_twi.h>
|
||||
#include <mach/board.h>
|
||||
#include <mach/cpu.h>
|
||||
|
||||
#define TWI_CLOCK 100000 /* Hz. max 400 Kbits/sec */
|
||||
|
||||
|
||||
static struct clk *twi_clk;
|
||||
static void __iomem *twi_base;
|
||||
|
||||
#define at91_twi_read(reg) __raw_readl(twi_base + (reg))
|
||||
#define at91_twi_write(reg, val) __raw_writel((val), twi_base + (reg))
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the TWI hardware registers.
|
||||
*/
|
||||
static void __devinit at91_twi_hwinit(void)
|
||||
{
|
||||
unsigned long cdiv, ckdiv;
|
||||
|
||||
at91_twi_write(AT91_TWI_IDR, 0xffffffff); /* Disable all interrupts */
|
||||
at91_twi_write(AT91_TWI_CR, AT91_TWI_SWRST); /* Reset peripheral */
|
||||
at91_twi_write(AT91_TWI_CR, AT91_TWI_MSEN); /* Set Master mode */
|
||||
|
||||
/* Calcuate clock dividers */
|
||||
cdiv = (clk_get_rate(twi_clk) / (2 * TWI_CLOCK)) - 3;
|
||||
cdiv = cdiv + 1; /* round up */
|
||||
ckdiv = 0;
|
||||
while (cdiv > 255) {
|
||||
ckdiv++;
|
||||
cdiv = cdiv >> 1;
|
||||
}
|
||||
|
||||
if (cpu_is_at91rm9200()) { /* AT91RM9200 Errata #22 */
|
||||
if (ckdiv > 5) {
|
||||
printk(KERN_ERR "AT91 I2C: Invalid TWI_CLOCK value!\n");
|
||||
ckdiv = 5;
|
||||
}
|
||||
}
|
||||
|
||||
at91_twi_write(AT91_TWI_CWGR, (ckdiv << 16) | (cdiv << 8) | cdiv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll the i2c status register until the specified bit is set.
|
||||
* Returns 0 if timed out (100 msec).
|
||||
*/
|
||||
static short at91_poll_status(unsigned long bit)
|
||||
{
|
||||
int loop_cntr = 10000;
|
||||
|
||||
do {
|
||||
udelay(10);
|
||||
} while (!(at91_twi_read(AT91_TWI_SR) & bit) && (--loop_cntr > 0));
|
||||
|
||||
return (loop_cntr > 0);
|
||||
}
|
||||
|
||||
static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length)
|
||||
{
|
||||
/* Send Start */
|
||||
at91_twi_write(AT91_TWI_CR, AT91_TWI_START);
|
||||
|
||||
/* Read data */
|
||||
while (length--) {
|
||||
if (!length) /* need to send Stop before reading last byte */
|
||||
at91_twi_write(AT91_TWI_CR, AT91_TWI_STOP);
|
||||
if (!at91_poll_status(AT91_TWI_RXRDY)) {
|
||||
dev_dbg(&adap->dev, "RXRDY timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
*buf++ = (at91_twi_read(AT91_TWI_RHR) & 0xff);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfer_write(struct i2c_adapter *adap, unsigned char *buf, int length)
|
||||
{
|
||||
/* Load first byte into transmitter */
|
||||
at91_twi_write(AT91_TWI_THR, *buf++);
|
||||
|
||||
/* Send Start */
|
||||
at91_twi_write(AT91_TWI_CR, AT91_TWI_START);
|
||||
|
||||
do {
|
||||
if (!at91_poll_status(AT91_TWI_TXRDY)) {
|
||||
dev_dbg(&adap->dev, "TXRDY timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
length--; /* byte was transmitted */
|
||||
|
||||
if (length > 0) /* more data to send? */
|
||||
at91_twi_write(AT91_TWI_THR, *buf++);
|
||||
} while (length);
|
||||
|
||||
/* Send Stop */
|
||||
at91_twi_write(AT91_TWI_CR, AT91_TWI_STOP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic i2c master transfer entrypoint.
|
||||
* i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
|
||||
*
|
||||
* Note: We do not use Atmel's feature of storing the "internal device address".
|
||||
* Instead the "internal device address" has to be written using a separate
|
||||
* i2c message.
|
||||
* http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html
|
||||
* Copyright (C) 2011 Weinmann Medical GmbH
|
||||
* Author: Nikolaus Voss <n.voss@weinmann.de>
|
||||
*
|
||||
* Evolved from original work by:
|
||||
* Copyright (C) 2004 Rick Bronson
|
||||
* Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
|
||||
*
|
||||
* Borrowed heavily from original work by:
|
||||
* Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static int at91_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg, int num)
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define TWI_CLK_HZ 100000 /* max 400 Kbits/s */
|
||||
#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
|
||||
|
||||
/* 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_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 completion cmd_complete;
|
||||
struct clk *clk;
|
||||
u8 *buf;
|
||||
size_t buf_len;
|
||||
struct i2c_msg *msg;
|
||||
int irq;
|
||||
unsigned transfer_status;
|
||||
struct i2c_adapter adapter;
|
||||
unsigned twi_cwgr_reg;
|
||||
struct at91_twi_pdata *pdata;
|
||||
};
|
||||
|
||||
static unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg)
|
||||
{
|
||||
int i, ret;
|
||||
return readl_relaxed(dev->base + reg);
|
||||
}
|
||||
|
||||
static void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
|
||||
{
|
||||
writel_relaxed(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 __devinit 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->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->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->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;
|
||||
|
||||
/* handle I2C_SMBUS_BLOCK_DATA */
|
||||
if (unlikely(dev->msg->flags & I2C_M_RECV_LEN)) {
|
||||
dev->msg->flags &= ~I2C_M_RECV_LEN;
|
||||
dev->buf_len += *dev->buf;
|
||||
dev->msg->len = dev->buf_len + 1;
|
||||
dev_dbg(dev->dev, "received block length %d\n", 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->dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len);
|
||||
|
||||
++dev->buf;
|
||||
}
|
||||
|
||||
static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct at91_twi_dev *dev = dev_id;
|
||||
const unsigned status = at91_twi_read(dev, AT91_TWI_SR);
|
||||
const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR);
|
||||
|
||||
if (!irqstatus)
|
||||
return IRQ_NONE;
|
||||
else if (irqstatus & AT91_TWI_RXRDY)
|
||||
at91_twi_read_next_byte(dev);
|
||||
else if (irqstatus & AT91_TWI_TXRDY)
|
||||
at91_twi_write_next_byte(dev);
|
||||
|
||||
/* catch error flags */
|
||||
dev->transfer_status |= status;
|
||||
|
||||
if (irqstatus & AT91_TWI_TXCOMP) {
|
||||
at91_disable_twi_interrupts(dev);
|
||||
complete(&dev->cmd_complete);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
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->dev, "transfer: %s %d bytes.\n",
|
||||
(dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len);
|
||||
|
||||
INIT_COMPLETION(dev->cmd_complete);
|
||||
dev->transfer_status = 0;
|
||||
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->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 && !(dev->msg->flags & I2C_M_RECV_LEN))
|
||||
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 = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
|
||||
dev->adapter.timeout);
|
||||
if (ret == 0) {
|
||||
dev_err(dev->dev, "controller timed out\n");
|
||||
at91_init_twi_bus(dev);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (dev->transfer_status & AT91_TWI_NACK) {
|
||||
dev_dbg(dev->dev, "received nack\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
if (dev->transfer_status & AT91_TWI_OVRE) {
|
||||
dev_err(dev->dev, "overrun while reading\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (has_unre_flag && dev->transfer_status & AT91_TWI_UNRE) {
|
||||
dev_err(dev->dev, "underrun while writing\n");
|
||||
return -EIO;
|
||||
}
|
||||
dev_dbg(dev->dev, "transfer complete\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
|
||||
{
|
||||
struct at91_twi_dev *dev = i2c_get_adapdata(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);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i,
|
||||
pmsg->flags & I2C_M_RD ? "read" : "writ",
|
||||
pmsg->len, pmsg->len > 1 ? "s" : "",
|
||||
pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr);
|
||||
/*
|
||||
* 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->dev,
|
||||
"cannot handle more than two concatenated messages.\n");
|
||||
return 0;
|
||||
} else if (num == 2) {
|
||||
int internal_address = 0;
|
||||
int i;
|
||||
|
||||
at91_twi_write(AT91_TWI_MMR, (pmsg->addr << 16)
|
||||
| ((pmsg->flags & I2C_M_RD) ? AT91_TWI_MREAD : 0));
|
||||
|
||||
if (pmsg->len && pmsg->buf) { /* sanity check */
|
||||
if (pmsg->flags & I2C_M_RD)
|
||||
ret = xfer_read(adap, pmsg->buf, pmsg->len);
|
||||
else
|
||||
ret = xfer_write(adap, pmsg->buf, pmsg->len);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait until transfer is finished */
|
||||
if (!at91_poll_status(AT91_TWI_TXCOMP)) {
|
||||
dev_dbg(&adap->dev, "TXCOMP timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
dev_err(dev->dev, "first transfer must be write.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_dbg(&adap->dev, "transfer complete\n");
|
||||
pmsg++; /* next message */
|
||||
if (msg->len > 3) {
|
||||
dev_err(dev->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);
|
||||
}
|
||||
return i;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return list of supported functionality.
|
||||
*/
|
||||
static u32 at91_func(struct i2c_adapter *adapter)
|
||||
static u32 at91_twi_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
|
||||
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
||||
}
|
||||
|
||||
static struct i2c_algorithm at91_algorithm = {
|
||||
.master_xfer = at91_xfer,
|
||||
.functionality = at91_func,
|
||||
static struct i2c_algorithm at91_twi_algorithm = {
|
||||
.master_xfer = at91_twi_xfer,
|
||||
.functionality = at91_twi_func,
|
||||
};
|
||||
|
||||
/*
|
||||
* Main initialization routine.
|
||||
*/
|
||||
static int __devinit at91_i2c_probe(struct platform_device *pdev)
|
||||
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 at91_twi_pdata at91sam9x5_config = {
|
||||
.clk_max_div = 7,
|
||||
.clk_offset = 4,
|
||||
.has_unre_flag = false,
|
||||
};
|
||||
|
||||
static const 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 */
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id atmel_twi_dt_ids[] = {
|
||||
{
|
||||
.compatible = "atmel,at91sam9260-i2c",
|
||||
.data = &at91sam9260_config,
|
||||
} , {
|
||||
.compatible = "atmel,at91sam9g20-i2c",
|
||||
.data = &at91sam9g20_config,
|
||||
} , {
|
||||
.compatible = "atmel,at91sam9g10-i2c",
|
||||
.data = &at91sam9g10_config,
|
||||
}, {
|
||||
.compatible = "atmel,at91sam9x5-i2c",
|
||||
.data = &at91sam9x5_config,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
|
||||
#else
|
||||
#define atmel_twi_dt_ids NULL
|
||||
#endif
|
||||
|
||||
static struct at91_twi_pdata * __devinit at91_twi_get_driver_data(
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_adapter *adapter;
|
||||
struct resource *res;
|
||||
int rc;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENXIO;
|
||||
|
||||
if (!request_mem_region(res->start, resource_size(res), "at91_i2c"))
|
||||
return -EBUSY;
|
||||
|
||||
twi_base = ioremap(res->start, resource_size(res));
|
||||
if (!twi_base) {
|
||||
rc = -ENOMEM;
|
||||
goto fail0;
|
||||
if (pdev->dev.of_node) {
|
||||
const struct of_device_id *match;
|
||||
match = of_match_node(atmel_twi_dt_ids, pdev->dev.of_node);
|
||||
if (!match)
|
||||
return NULL;
|
||||
return match->data;
|
||||
}
|
||||
|
||||
twi_clk = clk_get(NULL, "twi_clk");
|
||||
if (IS_ERR(twi_clk)) {
|
||||
dev_err(&pdev->dev, "no clock defined\n");
|
||||
rc = -ENODEV;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
|
||||
if (adapter == NULL) {
|
||||
dev_err(&pdev->dev, "can't allocate inteface!\n");
|
||||
rc = -ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
snprintf(adapter->name, sizeof(adapter->name), "AT91");
|
||||
adapter->algo = &at91_algorithm;
|
||||
adapter->class = I2C_CLASS_HWMON;
|
||||
adapter->dev.parent = &pdev->dev;
|
||||
/* adapter->id == 0 ... only one TWI controller for now */
|
||||
|
||||
platform_set_drvdata(pdev, adapter);
|
||||
|
||||
clk_enable(twi_clk); /* enable peripheral clock */
|
||||
at91_twi_hwinit(); /* initialize TWI controller */
|
||||
|
||||
rc = i2c_add_numbered_adapter(adapter);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "Adapter %s registration failed\n",
|
||||
adapter->name);
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "AT91 i2c bus driver.\n");
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(adapter);
|
||||
clk_disable(twi_clk);
|
||||
fail2:
|
||||
clk_put(twi_clk);
|
||||
fail1:
|
||||
iounmap(twi_base);
|
||||
fail0:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
return rc;
|
||||
return (struct at91_twi_pdata *) platform_get_device_id(pdev)->driver_data;
|
||||
}
|
||||
|
||||
static int __devexit at91_i2c_remove(struct platform_device *pdev)
|
||||
static int __devinit at91_twi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_adapter *adapter = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
struct at91_twi_dev *dev;
|
||||
struct resource *mem;
|
||||
int rc;
|
||||
|
||||
rc = i2c_del_adapter(adapter);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
init_completion(&dev->cmd_complete);
|
||||
dev->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
iounmap(twi_base);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem)
|
||||
return -ENODEV;
|
||||
|
||||
clk_disable(twi_clk); /* disable peripheral clock */
|
||||
clk_put(twi_clk);
|
||||
dev->pdata = at91_twi_get_driver_data(pdev);
|
||||
if (!dev->pdata)
|
||||
return -ENODEV;
|
||||
|
||||
dev->base = devm_request_and_ioremap(&pdev->dev, mem);
|
||||
if (!dev->base)
|
||||
return -EBUSY;
|
||||
|
||||
dev->irq = platform_get_irq(pdev, 0);
|
||||
if (dev->irq < 0)
|
||||
return dev->irq;
|
||||
|
||||
rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt, 0,
|
||||
dev_name(dev->dev), dev);
|
||||
if (rc) {
|
||||
dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
dev->clk = devm_clk_get(dev->dev, NULL);
|
||||
if (IS_ERR(dev->clk)) {
|
||||
dev_err(dev->dev, "no clock defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
clk_prepare_enable(dev->clk);
|
||||
|
||||
at91_calc_twi_clock(dev, TWI_CLK_HZ);
|
||||
at91_init_twi_bus(dev);
|
||||
|
||||
snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
|
||||
i2c_set_adapdata(&dev->adapter, dev);
|
||||
dev->adapter.owner = THIS_MODULE;
|
||||
dev->adapter.class = I2C_CLASS_HWMON;
|
||||
dev->adapter.algo = &at91_twi_algorithm;
|
||||
dev->adapter.dev.parent = dev->dev;
|
||||
dev->adapter.nr = pdev->id;
|
||||
dev->adapter.timeout = AT91_I2C_TIMEOUT;
|
||||
dev->adapter.dev.of_node = pdev->dev.of_node;
|
||||
|
||||
rc = i2c_add_numbered_adapter(&dev->adapter);
|
||||
if (rc) {
|
||||
dev_err(dev->dev, "Adapter %s registration failed\n",
|
||||
dev->adapter.name);
|
||||
clk_disable_unprepare(dev->clk);
|
||||
return rc;
|
||||
}
|
||||
|
||||
of_i2c_register_devices(&dev->adapter);
|
||||
|
||||
dev_info(dev->dev, "AT91 i2c bus driver.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit at91_twi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct at91_twi_dev *dev = platform_get_drvdata(pdev);
|
||||
int rc;
|
||||
|
||||
rc = i2c_del_adapter(&dev->adapter);
|
||||
clk_disable_unprepare(dev->clk);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* NOTE: could save a few mA by keeping clock off outside of at91_xfer... */
|
||||
|
||||
static int at91_i2c_suspend(struct device *dev)
|
||||
static int at91_twi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
clk_disable(twi_clk);
|
||||
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(twi_dev->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_i2c_resume(struct device *dev)
|
||||
static int at91_twi_runtime_resume(struct device *dev)
|
||||
{
|
||||
return clk_enable(twi_clk);
|
||||
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||
|
||||
return clk_enable(twi_dev->clk);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(at91_i2c_pm, at91_i2c_suspend, at91_i2c_resume);
|
||||
#define AT91_I2C_PM (&at91_i2c_pm)
|
||||
static const struct dev_pm_ops at91_twi_pm = {
|
||||
.runtime_suspend = at91_twi_runtime_suspend,
|
||||
.runtime_resume = at91_twi_runtime_resume,
|
||||
};
|
||||
|
||||
#define at91_twi_pm_ops (&at91_twi_pm)
|
||||
#else
|
||||
#define AT91_I2C_PM NULL
|
||||
#define at91_twi_pm_ops NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver at91_i2c_driver = {
|
||||
.probe = at91_i2c_probe,
|
||||
.remove = __devexit_p(at91_i2c_remove),
|
||||
static struct platform_driver at91_twi_driver = {
|
||||
.probe = at91_twi_probe,
|
||||
.remove = __devexit_p(at91_twi_remove),
|
||||
.id_table = at91_twi_devtypes,
|
||||
.driver = {
|
||||
.name = "at91_i2c",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = AT91_I2C_PM,
|
||||
.of_match_table = atmel_twi_dt_ids,
|
||||
.pm = at91_twi_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(at91_i2c_driver);
|
||||
static int __init at91_twi_init(void)
|
||||
{
|
||||
return platform_driver_register(&at91_twi_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Rick Bronson");
|
||||
static void __exit at91_twi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&at91_twi_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(at91_twi_init);
|
||||
module_exit(at91_twi_exit);
|
||||
|
||||
MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
|
||||
MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:at91_i2c");
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_i2c.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <linux/platform_data/i2c-davinci.h>
|
||||
|
@ -114,6 +116,7 @@ struct davinci_i2c_dev {
|
|||
struct completion xfr_complete;
|
||||
struct notifier_block freq_transition;
|
||||
#endif
|
||||
struct davinci_i2c_platform_data *pdata;
|
||||
};
|
||||
|
||||
/* default platform data to use if not supplied in the platform_device */
|
||||
|
@ -155,7 +158,7 @@ static void generic_i2c_clock_pulse(unsigned int scl_pin)
|
|||
static void i2c_recover_bus(struct davinci_i2c_dev *dev)
|
||||
{
|
||||
u32 flag = 0;
|
||||
struct davinci_i2c_platform_data *pdata = dev->dev->platform_data;
|
||||
struct davinci_i2c_platform_data *pdata = dev->pdata;
|
||||
|
||||
dev_err(dev->dev, "initiating i2c bus recovery\n");
|
||||
/* Send NACK to the slave */
|
||||
|
@ -163,8 +166,7 @@ static void i2c_recover_bus(struct davinci_i2c_dev *dev)
|
|||
flag |= DAVINCI_I2C_MDR_NACK;
|
||||
/* write the data into mode register */
|
||||
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
|
||||
if (pdata)
|
||||
generic_i2c_clock_pulse(pdata->scl_pin);
|
||||
generic_i2c_clock_pulse(pdata->scl_pin);
|
||||
/* Send STOP */
|
||||
flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
|
||||
flag |= DAVINCI_I2C_MDR_STP;
|
||||
|
@ -187,7 +189,7 @@ static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_dev *i2c_dev,
|
|||
|
||||
static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
|
||||
{
|
||||
struct davinci_i2c_platform_data *pdata = dev->dev->platform_data;
|
||||
struct davinci_i2c_platform_data *pdata = dev->pdata;
|
||||
u16 psc;
|
||||
u32 clk;
|
||||
u32 d;
|
||||
|
@ -235,10 +237,7 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
|
|||
*/
|
||||
static int i2c_davinci_init(struct davinci_i2c_dev *dev)
|
||||
{
|
||||
struct davinci_i2c_platform_data *pdata = dev->dev->platform_data;
|
||||
|
||||
if (!pdata)
|
||||
pdata = &davinci_i2c_platform_data_default;
|
||||
struct davinci_i2c_platform_data *pdata = dev->pdata;
|
||||
|
||||
/* put I2C into reset */
|
||||
davinci_i2c_reset_ctrl(dev, 0);
|
||||
|
@ -260,6 +259,7 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
|
|||
dev_dbg(dev->dev, "bus_freq = %dkHz, bus_delay = %d\n",
|
||||
pdata->bus_freq, pdata->bus_delay);
|
||||
|
||||
|
||||
/* Take the I2C module out of reset: */
|
||||
davinci_i2c_reset_ctrl(dev, 1);
|
||||
|
||||
|
@ -308,13 +308,11 @@ static int
|
|||
i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
|
||||
{
|
||||
struct davinci_i2c_dev *dev = i2c_get_adapdata(adap);
|
||||
struct davinci_i2c_platform_data *pdata = dev->dev->platform_data;
|
||||
struct davinci_i2c_platform_data *pdata = dev->pdata;
|
||||
u32 flag;
|
||||
u16 w;
|
||||
int r;
|
||||
|
||||
if (!pdata)
|
||||
pdata = &davinci_i2c_platform_data_default;
|
||||
/* Introduce a delay, required for some boards (e.g Davinci EVM) */
|
||||
if (pdata->bus_delay)
|
||||
udelay(pdata->bus_delay);
|
||||
|
@ -635,6 +633,12 @@ static struct i2c_algorithm i2c_davinci_algo = {
|
|||
.functionality = i2c_davinci_func,
|
||||
};
|
||||
|
||||
static const struct of_device_id davinci_i2c_of_match[] = {
|
||||
{.compatible = "ti,davinci-i2c", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, davinci_i2c_of_match);
|
||||
|
||||
static int davinci_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_i2c_dev *dev;
|
||||
|
@ -674,14 +678,33 @@ static int davinci_i2c_probe(struct platform_device *pdev)
|
|||
#endif
|
||||
dev->dev = get_device(&pdev->dev);
|
||||
dev->irq = irq->start;
|
||||
dev->pdata = dev->dev->platform_data;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
if (!dev->pdata && pdev->dev.of_node) {
|
||||
u32 prop;
|
||||
|
||||
dev->pdata = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct davinci_i2c_platform_data), GFP_KERNEL);
|
||||
if (!dev->pdata) {
|
||||
r = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
memcpy(dev->pdata, &davinci_i2c_platform_data_default,
|
||||
sizeof(struct davinci_i2c_platform_data));
|
||||
if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||
&prop))
|
||||
dev->pdata->bus_freq = prop / 1000;
|
||||
} else if (!dev->pdata) {
|
||||
dev->pdata = &davinci_i2c_platform_data_default;
|
||||
}
|
||||
|
||||
dev->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(dev->clk)) {
|
||||
r = -ENODEV;
|
||||
goto err_free_mem;
|
||||
}
|
||||
clk_enable(dev->clk);
|
||||
clk_prepare_enable(dev->clk);
|
||||
|
||||
dev->base = ioremap(mem->start, resource_size(mem));
|
||||
if (!dev->base) {
|
||||
|
@ -711,6 +734,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
|
|||
adap->algo = &i2c_davinci_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
adap->timeout = DAVINCI_I2C_TIMEOUT;
|
||||
adap->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
adap->nr = pdev->id;
|
||||
r = i2c_add_numbered_adapter(adap);
|
||||
|
@ -718,6 +742,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "failure adding adapter\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
of_i2c_register_devices(adap);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -726,7 +751,7 @@ err_free_irq:
|
|||
err_unuse_clocks:
|
||||
iounmap(dev->base);
|
||||
err_mem_ioremap:
|
||||
clk_disable(dev->clk);
|
||||
clk_disable_unprepare(dev->clk);
|
||||
clk_put(dev->clk);
|
||||
dev->clk = NULL;
|
||||
err_free_mem:
|
||||
|
@ -750,7 +775,7 @@ static int davinci_i2c_remove(struct platform_device *pdev)
|
|||
i2c_del_adapter(&dev->adapter);
|
||||
put_device(&pdev->dev);
|
||||
|
||||
clk_disable(dev->clk);
|
||||
clk_disable_unprepare(dev->clk);
|
||||
clk_put(dev->clk);
|
||||
dev->clk = NULL;
|
||||
|
||||
|
@ -772,7 +797,7 @@ static int davinci_i2c_suspend(struct device *dev)
|
|||
|
||||
/* put I2C into reset */
|
||||
davinci_i2c_reset_ctrl(i2c_dev, 0);
|
||||
clk_disable(i2c_dev->clk);
|
||||
clk_disable_unprepare(i2c_dev->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -782,7 +807,7 @@ static int davinci_i2c_resume(struct device *dev)
|
|||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct davinci_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
||||
|
||||
clk_enable(i2c_dev->clk);
|
||||
clk_prepare_enable(i2c_dev->clk);
|
||||
/* take I2C out of reset */
|
||||
davinci_i2c_reset_ctrl(i2c_dev, 1);
|
||||
|
||||
|
@ -809,6 +834,7 @@ static struct platform_driver davinci_i2c_driver = {
|
|||
.name = "i2c_davinci",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = davinci_i2c_pm_ops,
|
||||
.of_match_table = of_match_ptr(davinci_i2c_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -272,9 +272,9 @@ static void __init i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
|
|||
|
||||
/* dev_dbg() can't be used, because adapter is not yet registered */
|
||||
#ifdef CONFIG_I2C_DEBUG_BUS
|
||||
printk(KERN_DEBUG "I2C: <%s> I2C_CLK=%d, REQ DIV=%d\n",
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n",
|
||||
__func__, i2c_clk_rate, div);
|
||||
printk(KERN_DEBUG "I2C: <%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
|
||||
__func__, i2c_clk_div[i][1], i2c_clk_div[i][0]);
|
||||
#endif
|
||||
}
|
||||
|
@ -564,7 +564,7 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
|
|||
resource_size(res), res->start);
|
||||
dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
|
||||
i2c_imx->adapter.name);
|
||||
dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
|
||||
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
|
||||
|
||||
return 0; /* Return OK */
|
||||
}
|
||||
|
|
|
@ -576,7 +576,23 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|||
mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
|
||||
}
|
||||
}
|
||||
mpc_i2c_stop(i2c);
|
||||
mpc_i2c_stop(i2c); /* Initiate STOP */
|
||||
orig_jiffies = jiffies;
|
||||
/* Wait until STOP is seen, allow up to 1 s */
|
||||
while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
|
||||
if (time_after(jiffies, orig_jiffies + HZ)) {
|
||||
u8 status = readb(i2c->base + MPC_I2C_SR);
|
||||
|
||||
dev_dbg(i2c->dev, "timeout\n");
|
||||
if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) {
|
||||
writeb(status & ~CSR_MAL,
|
||||
i2c->base + MPC_I2C_SR);
|
||||
mpc_i2c_fixup(i2c);
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
cond_resched();
|
||||
}
|
||||
return (ret < 0) ? ret : num;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
*
|
||||
* Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*
|
||||
* TODO: add dma-support if platform-support for it is available
|
||||
*
|
||||
* 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
|
||||
|
@ -31,9 +29,16 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_i2c.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/fsl/mxs-dma.h>
|
||||
|
||||
#define DRIVER_NAME "mxs-i2c"
|
||||
|
||||
static bool use_pioqueue;
|
||||
module_param(use_pioqueue, bool, 0);
|
||||
MODULE_PARM_DESC(use_pioqueue, "Use PIOQUEUE mode for transfer instead of DMA");
|
||||
|
||||
#define MXS_I2C_CTRL0 (0x00)
|
||||
#define MXS_I2C_CTRL0_SET (0x04)
|
||||
|
||||
|
@ -146,6 +151,16 @@ struct mxs_i2c_dev {
|
|||
u32 cmd_err;
|
||||
struct i2c_adapter adapter;
|
||||
const struct mxs_i2c_speed_config *speed;
|
||||
|
||||
/* DMA support components */
|
||||
bool dma_mode;
|
||||
int dma_channel;
|
||||
struct dma_chan *dmach;
|
||||
struct mxs_dma_data dma_data;
|
||||
uint32_t pio_data[2];
|
||||
uint32_t addr_data;
|
||||
struct scatterlist sg_io[2];
|
||||
bool dma_read;
|
||||
};
|
||||
|
||||
static void mxs_i2c_reset(struct mxs_i2c_dev *i2c)
|
||||
|
@ -157,7 +172,11 @@ static void mxs_i2c_reset(struct mxs_i2c_dev *i2c)
|
|||
writel(i2c->speed->timing2, i2c->regs + MXS_I2C_TIMING2);
|
||||
|
||||
writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET);
|
||||
writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE,
|
||||
if (i2c->dma_mode)
|
||||
writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE,
|
||||
i2c->regs + MXS_I2C_QUEUECTRL_CLR);
|
||||
else
|
||||
writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE,
|
||||
i2c->regs + MXS_I2C_QUEUECTRL_SET);
|
||||
}
|
||||
|
||||
|
@ -248,6 +267,150 @@ static int mxs_i2c_finish_read(struct mxs_i2c_dev *i2c, u8 *buf, int len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mxs_i2c_dma_finish(struct mxs_i2c_dev *i2c)
|
||||
{
|
||||
if (i2c->dma_read) {
|
||||
dma_unmap_sg(i2c->dev, &i2c->sg_io[0], 1, DMA_TO_DEVICE);
|
||||
dma_unmap_sg(i2c->dev, &i2c->sg_io[1], 1, DMA_FROM_DEVICE);
|
||||
} else {
|
||||
dma_unmap_sg(i2c->dev, i2c->sg_io, 2, DMA_TO_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
static void mxs_i2c_dma_irq_callback(void *param)
|
||||
{
|
||||
struct mxs_i2c_dev *i2c = param;
|
||||
|
||||
complete(&i2c->cmd_complete);
|
||||
mxs_i2c_dma_finish(i2c);
|
||||
}
|
||||
|
||||
static int mxs_i2c_dma_setup_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msg, uint32_t flags)
|
||||
{
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
|
||||
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
i2c->dma_read = 1;
|
||||
i2c->addr_data = (msg->addr << 1) | I2C_SMBUS_READ;
|
||||
|
||||
/*
|
||||
* SELECT command.
|
||||
*/
|
||||
|
||||
/* Queue the PIO register write transfer. */
|
||||
i2c->pio_data[0] = MXS_CMD_I2C_SELECT;
|
||||
desc = dmaengine_prep_slave_sg(i2c->dmach,
|
||||
(struct scatterlist *)&i2c->pio_data[0],
|
||||
1, DMA_TRANS_NONE, 0);
|
||||
if (!desc) {
|
||||
dev_err(i2c->dev,
|
||||
"Failed to get PIO reg. write descriptor.\n");
|
||||
goto select_init_pio_fail;
|
||||
}
|
||||
|
||||
/* Queue the DMA data transfer. */
|
||||
sg_init_one(&i2c->sg_io[0], &i2c->addr_data, 1);
|
||||
dma_map_sg(i2c->dev, &i2c->sg_io[0], 1, DMA_TO_DEVICE);
|
||||
desc = dmaengine_prep_slave_sg(i2c->dmach, &i2c->sg_io[0], 1,
|
||||
DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
dev_err(i2c->dev,
|
||||
"Failed to get DMA data write descriptor.\n");
|
||||
goto select_init_dma_fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* READ command.
|
||||
*/
|
||||
|
||||
/* Queue the PIO register write transfer. */
|
||||
i2c->pio_data[1] = flags | MXS_CMD_I2C_READ |
|
||||
MXS_I2C_CTRL0_XFER_COUNT(msg->len);
|
||||
desc = dmaengine_prep_slave_sg(i2c->dmach,
|
||||
(struct scatterlist *)&i2c->pio_data[1],
|
||||
1, DMA_TRANS_NONE, DMA_PREP_INTERRUPT);
|
||||
if (!desc) {
|
||||
dev_err(i2c->dev,
|
||||
"Failed to get PIO reg. write descriptor.\n");
|
||||
goto select_init_dma_fail;
|
||||
}
|
||||
|
||||
/* Queue the DMA data transfer. */
|
||||
sg_init_one(&i2c->sg_io[1], msg->buf, msg->len);
|
||||
dma_map_sg(i2c->dev, &i2c->sg_io[1], 1, DMA_FROM_DEVICE);
|
||||
desc = dmaengine_prep_slave_sg(i2c->dmach, &i2c->sg_io[1], 1,
|
||||
DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
dev_err(i2c->dev,
|
||||
"Failed to get DMA data write descriptor.\n");
|
||||
goto read_init_dma_fail;
|
||||
}
|
||||
} else {
|
||||
i2c->dma_read = 0;
|
||||
i2c->addr_data = (msg->addr << 1) | I2C_SMBUS_WRITE;
|
||||
|
||||
/*
|
||||
* WRITE command.
|
||||
*/
|
||||
|
||||
/* Queue the PIO register write transfer. */
|
||||
i2c->pio_data[0] = flags | MXS_CMD_I2C_WRITE |
|
||||
MXS_I2C_CTRL0_XFER_COUNT(msg->len + 1);
|
||||
desc = dmaengine_prep_slave_sg(i2c->dmach,
|
||||
(struct scatterlist *)&i2c->pio_data[0],
|
||||
1, DMA_TRANS_NONE, 0);
|
||||
if (!desc) {
|
||||
dev_err(i2c->dev,
|
||||
"Failed to get PIO reg. write descriptor.\n");
|
||||
goto write_init_pio_fail;
|
||||
}
|
||||
|
||||
/* Queue the DMA data transfer. */
|
||||
sg_init_table(i2c->sg_io, 2);
|
||||
sg_set_buf(&i2c->sg_io[0], &i2c->addr_data, 1);
|
||||
sg_set_buf(&i2c->sg_io[1], msg->buf, msg->len);
|
||||
dma_map_sg(i2c->dev, i2c->sg_io, 2, DMA_TO_DEVICE);
|
||||
desc = dmaengine_prep_slave_sg(i2c->dmach, i2c->sg_io, 2,
|
||||
DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
dev_err(i2c->dev,
|
||||
"Failed to get DMA data write descriptor.\n");
|
||||
goto write_init_dma_fail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The last descriptor must have this callback,
|
||||
* to finish the DMA transaction.
|
||||
*/
|
||||
desc->callback = mxs_i2c_dma_irq_callback;
|
||||
desc->callback_param = i2c;
|
||||
|
||||
/* Start the transfer. */
|
||||
dmaengine_submit(desc);
|
||||
dma_async_issue_pending(i2c->dmach);
|
||||
return 0;
|
||||
|
||||
/* Read failpath. */
|
||||
read_init_dma_fail:
|
||||
dma_unmap_sg(i2c->dev, &i2c->sg_io[1], 1, DMA_FROM_DEVICE);
|
||||
select_init_dma_fail:
|
||||
dma_unmap_sg(i2c->dev, &i2c->sg_io[0], 1, DMA_TO_DEVICE);
|
||||
select_init_pio_fail:
|
||||
return -EINVAL;
|
||||
|
||||
/* Write failpath. */
|
||||
write_init_dma_fail:
|
||||
dma_unmap_sg(i2c->dev, i2c->sg_io, 2, DMA_TO_DEVICE);
|
||||
write_init_pio_fail:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Low level master read/write transaction.
|
||||
*/
|
||||
|
@ -258,6 +421,8 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
|
|||
int ret;
|
||||
int flags;
|
||||
|
||||
flags = stop ? MXS_I2C_CTRL0_POST_SEND_STOP : 0;
|
||||
|
||||
dev_dbg(i2c->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
|
||||
msg->addr, msg->len, msg->flags, stop);
|
||||
|
||||
|
@ -267,23 +432,29 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
|
|||
init_completion(&i2c->cmd_complete);
|
||||
i2c->cmd_err = 0;
|
||||
|
||||
flags = stop ? MXS_I2C_CTRL0_POST_SEND_STOP : 0;
|
||||
if (i2c->dma_mode) {
|
||||
ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
mxs_i2c_pioq_setup_read(i2c, msg->addr,
|
||||
msg->len, flags);
|
||||
} else {
|
||||
mxs_i2c_pioq_setup_write(i2c, msg->addr, msg->buf,
|
||||
msg->len, flags);
|
||||
}
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
mxs_i2c_pioq_setup_read(i2c, msg->addr, msg->len, flags);
|
||||
else
|
||||
mxs_i2c_pioq_setup_write(i2c, msg->addr, msg->buf, msg->len,
|
||||
flags);
|
||||
|
||||
writel(MXS_I2C_QUEUECTRL_QUEUE_RUN,
|
||||
writel(MXS_I2C_QUEUECTRL_QUEUE_RUN,
|
||||
i2c->regs + MXS_I2C_QUEUECTRL_SET);
|
||||
}
|
||||
|
||||
ret = wait_for_completion_timeout(&i2c->cmd_complete,
|
||||
msecs_to_jiffies(1000));
|
||||
if (ret == 0)
|
||||
goto timeout;
|
||||
|
||||
if ((!i2c->cmd_err) && (msg->flags & I2C_M_RD)) {
|
||||
if (!i2c->dma_mode && !i2c->cmd_err && (msg->flags & I2C_M_RD)) {
|
||||
ret = mxs_i2c_finish_read(i2c, msg->buf, msg->len);
|
||||
if (ret)
|
||||
goto timeout;
|
||||
|
@ -301,6 +472,8 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
|
|||
|
||||
timeout:
|
||||
dev_dbg(i2c->dev, "Timeout!\n");
|
||||
if (i2c->dma_mode)
|
||||
mxs_i2c_dma_finish(i2c);
|
||||
mxs_i2c_reset(i2c);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
@ -342,11 +515,13 @@ static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id)
|
|||
/* MXS_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ is only for slaves */
|
||||
i2c->cmd_err = -EIO;
|
||||
|
||||
is_last_cmd = (readl(i2c->regs + MXS_I2C_QUEUESTAT) &
|
||||
MXS_I2C_QUEUESTAT_WRITE_QUEUE_CNT_MASK) == 0;
|
||||
if (!i2c->dma_mode) {
|
||||
is_last_cmd = (readl(i2c->regs + MXS_I2C_QUEUESTAT) &
|
||||
MXS_I2C_QUEUESTAT_WRITE_QUEUE_CNT_MASK) == 0;
|
||||
|
||||
if (is_last_cmd || i2c->cmd_err)
|
||||
complete(&i2c->cmd_complete);
|
||||
if (is_last_cmd || i2c->cmd_err)
|
||||
complete(&i2c->cmd_complete);
|
||||
}
|
||||
|
||||
writel(stat, i2c->regs + MXS_I2C_CTRL1_CLR);
|
||||
|
||||
|
@ -358,6 +533,21 @@ static const struct i2c_algorithm mxs_i2c_algo = {
|
|||
.functionality = mxs_i2c_func,
|
||||
};
|
||||
|
||||
static bool mxs_i2c_dma_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
struct mxs_i2c_dev *i2c = param;
|
||||
|
||||
if (!mxs_dma_is_apbx(chan))
|
||||
return false;
|
||||
|
||||
if (chan->chan_id != i2c->dma_channel)
|
||||
return false;
|
||||
|
||||
chan->private = &i2c->dma_data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
|
||||
{
|
||||
uint32_t speed;
|
||||
|
@ -365,6 +555,26 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
|
|||
struct device_node *node = dev->of_node;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The MXS I2C DMA mode is prefered and enabled by default.
|
||||
* The PIO mode is still supported, but should be used only
|
||||
* for debuging purposes etc.
|
||||
*/
|
||||
i2c->dma_mode = !use_pioqueue;
|
||||
if (!i2c->dma_mode)
|
||||
dev_info(dev, "Using PIOQUEUE mode for I2C transfers!\n");
|
||||
|
||||
/*
|
||||
* TODO: This is a temporary solution and should be changed
|
||||
* to use generic DMA binding later when the helpers get in.
|
||||
*/
|
||||
ret = of_property_read_u32(node, "fsl,i2c-dma-channel",
|
||||
&i2c->dma_channel);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Failed to get DMA channel, using PIOQUEUE!\n");
|
||||
i2c->dma_mode = 0;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "clock-frequency", &speed);
|
||||
if (ret)
|
||||
dev_warn(dev, "No I2C speed selected, using 100kHz\n");
|
||||
|
@ -384,7 +594,8 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev)
|
|||
struct pinctrl *pinctrl;
|
||||
struct resource *res;
|
||||
resource_size_t res_size;
|
||||
int err, irq;
|
||||
int err, irq, dmairq;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(dev);
|
||||
if (IS_ERR(pinctrl))
|
||||
|
@ -395,7 +606,10 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
dmairq = platform_get_irq(pdev, 1);
|
||||
|
||||
if (!res || irq < 0 || dmairq < 0)
|
||||
return -ENOENT;
|
||||
|
||||
res_size = resource_size(res);
|
||||
|
@ -406,10 +620,6 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev)
|
|||
if (!i2c->regs)
|
||||
return -EBUSY;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_request_irq(dev, irq, mxs_i2c_isr, 0, dev_name(dev), i2c);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -423,6 +633,18 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
/* Setup the DMA */
|
||||
if (i2c->dma_mode) {
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
i2c->dma_data.chan_irq = dmairq;
|
||||
i2c->dmach = dma_request_channel(mask, mxs_i2c_dma_filter, i2c);
|
||||
if (!i2c->dmach) {
|
||||
dev_err(dev, "Failed to request dma\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
|
||||
/* Do reset to enforce correct startup after pinmuxing */
|
||||
|
@ -458,6 +680,9 @@ static int __devexit mxs_i2c_remove(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return -EBUSY;
|
||||
|
||||
if (i2c->dmach)
|
||||
dma_release_channel(i2c->dmach);
|
||||
|
||||
writel(MXS_I2C_CTRL0_SFTRST, i2c->regs + MXS_I2C_CTRL0_SET);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
|
|
@ -22,9 +22,10 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_data/i2c-nomadik.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_i2c.h>
|
||||
|
||||
#define DRIVER_NAME "nmk-i2c"
|
||||
|
||||
|
@ -146,7 +147,6 @@ struct i2c_nmk_client {
|
|||
* @stop: stop condition.
|
||||
* @xfer_complete: acknowledge completion for a I2C message.
|
||||
* @result: controller propogated result.
|
||||
* @regulator: pointer to i2c regulator.
|
||||
* @busy: Busy doing transfer.
|
||||
*/
|
||||
struct nmk_i2c_dev {
|
||||
|
@ -160,7 +160,6 @@ struct nmk_i2c_dev {
|
|||
int stop;
|
||||
struct completion xfer_complete;
|
||||
int result;
|
||||
struct regulator *regulator;
|
||||
bool busy;
|
||||
};
|
||||
|
||||
|
@ -643,8 +642,6 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
|
|||
|
||||
dev->busy = true;
|
||||
|
||||
if (dev->regulator)
|
||||
regulator_enable(dev->regulator);
|
||||
pm_runtime_get_sync(&dev->adev->dev);
|
||||
|
||||
clk_enable(dev->clk);
|
||||
|
@ -676,8 +673,6 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
|
|||
out:
|
||||
clk_disable(dev->clk);
|
||||
pm_runtime_put_sync(&dev->adev->dev);
|
||||
if (dev->regulator)
|
||||
regulator_disable(dev->regulator);
|
||||
|
||||
dev->busy = false;
|
||||
|
||||
|
@ -920,18 +915,42 @@ static struct nmk_i2c_controller u8500_i2c = {
|
|||
.sm = I2C_FREQ_MODE_FAST,
|
||||
};
|
||||
|
||||
static void nmk_i2c_of_probe(struct device_node *np,
|
||||
struct nmk_i2c_controller *pdata)
|
||||
{
|
||||
of_property_read_u32(np, "clock-frequency", &pdata->clk_freq);
|
||||
|
||||
/* This driver only supports 'standard' and 'fast' modes of operation. */
|
||||
if (pdata->clk_freq <= 100000)
|
||||
pdata->sm = I2C_FREQ_MODE_STANDARD;
|
||||
else
|
||||
pdata->sm = I2C_FREQ_MODE_FAST;
|
||||
}
|
||||
|
||||
static atomic_t adapter_id = ATOMIC_INIT(0);
|
||||
|
||||
static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct nmk_i2c_controller *pdata = adev->dev.platform_data;
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
struct nmk_i2c_dev *dev;
|
||||
struct i2c_adapter *adap;
|
||||
|
||||
if (!pdata)
|
||||
/* No i2c configuration found, using the default. */
|
||||
pdata = &u8500_i2c;
|
||||
if (!pdata) {
|
||||
if (np) {
|
||||
pdata = devm_kzalloc(&adev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
ret = -ENOMEM;
|
||||
goto err_no_mem;
|
||||
}
|
||||
/* Provide the default configuration as a base. */
|
||||
memcpy(pdata, &u8500_i2c, sizeof(struct nmk_i2c_controller));
|
||||
nmk_i2c_of_probe(np, pdata);
|
||||
} else
|
||||
/* No i2c configuration found, using the default. */
|
||||
pdata = &u8500_i2c;
|
||||
}
|
||||
|
||||
dev = kzalloc(sizeof(struct nmk_i2c_dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
|
@ -957,12 +976,6 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
goto err_irq;
|
||||
}
|
||||
|
||||
dev->regulator = regulator_get(&adev->dev, "v-i2c");
|
||||
if (IS_ERR(dev->regulator)) {
|
||||
dev_warn(&adev->dev, "could not get i2c regulator\n");
|
||||
dev->regulator = NULL;
|
||||
}
|
||||
|
||||
pm_suspend_ignore_children(&adev->dev, true);
|
||||
|
||||
dev->clk = clk_get(&adev->dev, NULL);
|
||||
|
@ -973,6 +986,7 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
}
|
||||
|
||||
adap = &dev->adap;
|
||||
adap->dev.of_node = np;
|
||||
adap->dev.parent = &adev->dev;
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
|
@ -1002,6 +1016,8 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
goto err_add_adap;
|
||||
}
|
||||
|
||||
of_i2c_register_devices(adap);
|
||||
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
return 0;
|
||||
|
@ -1009,8 +1025,6 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
err_add_adap:
|
||||
clk_put(dev->clk);
|
||||
err_no_clk:
|
||||
if (dev->regulator)
|
||||
regulator_put(dev->regulator);
|
||||
free_irq(dev->irq, dev);
|
||||
err_irq:
|
||||
iounmap(dev->virtbase);
|
||||
|
@ -1038,8 +1052,6 @@ static int nmk_i2c_remove(struct amba_device *adev)
|
|||
if (res)
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
clk_put(dev->clk);
|
||||
if (dev->regulator)
|
||||
regulator_put(dev->regulator);
|
||||
pm_runtime_disable(&adev->dev);
|
||||
amba_set_drvdata(adev, NULL);
|
||||
kfree(dev);
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/i2c-omap.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
/* I2C controller revisions */
|
||||
#define OMAP_I2C_OMAP1_REV_2 0x20
|
||||
|
@ -55,6 +56,9 @@
|
|||
/* timeout waiting for the controller to respond */
|
||||
#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
|
||||
|
||||
/* timeout for pm runtime autosuspend */
|
||||
#define OMAP_I2C_PM_TIMEOUT 1000 /* ms */
|
||||
|
||||
/* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */
|
||||
enum {
|
||||
OMAP_I2C_REV_REG = 0,
|
||||
|
@ -176,15 +180,15 @@ enum {
|
|||
#define I2C_OMAP_ERRATA_I462 (1 << 1)
|
||||
|
||||
struct omap_i2c_dev {
|
||||
spinlock_t lock; /* IRQ synchronization */
|
||||
struct device *dev;
|
||||
void __iomem *base; /* virtual */
|
||||
int irq;
|
||||
int reg_shift; /* bit shift for I2C register addresses */
|
||||
struct completion cmd_complete;
|
||||
struct resource *ioarea;
|
||||
u32 latency; /* maximum mpu wkup latency */
|
||||
void (*set_mpu_wkup_lat)(struct device *dev,
|
||||
long latency);
|
||||
u32 latency; /* maximum MPU wkup latency */
|
||||
struct pm_qos_request pm_qos_request;
|
||||
u32 speed; /* Speed of bus in kHz */
|
||||
u32 dtrev; /* extra revision from DT */
|
||||
u32 flags;
|
||||
|
@ -193,12 +197,14 @@ struct omap_i2c_dev {
|
|||
u8 *regs;
|
||||
size_t buf_len;
|
||||
struct i2c_adapter adapter;
|
||||
u8 threshold;
|
||||
u8 fifo_size; /* use as flag and value
|
||||
* fifo_size==0 implies no fifo
|
||||
* if set, should be trsh+1
|
||||
*/
|
||||
u8 rev;
|
||||
unsigned b_hw:1; /* bad h/w fixes */
|
||||
unsigned receiver:1; /* true when we're in receiver mode */
|
||||
u16 iestate; /* Saved interrupt register */
|
||||
u16 pscstate;
|
||||
u16 scllstate;
|
||||
|
@ -417,13 +423,6 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
|
|||
omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll);
|
||||
omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh);
|
||||
|
||||
if (dev->fifo_size) {
|
||||
/* Note: setup required fifo size - 1. RTRSH and XTRSH */
|
||||
buf = (dev->fifo_size - 1) << 8 | OMAP_I2C_BUF_RXFIF_CLR |
|
||||
(dev->fifo_size - 1) | OMAP_I2C_BUF_TXFIF_CLR;
|
||||
omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, buf);
|
||||
}
|
||||
|
||||
/* Take the I2C module out of reset: */
|
||||
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
|
||||
|
||||
|
@ -461,6 +460,43 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void omap_i2c_resize_fifo(struct omap_i2c_dev *dev, u8 size, bool is_rx)
|
||||
{
|
||||
u16 buf;
|
||||
|
||||
if (dev->flags & OMAP_I2C_FLAG_NO_FIFO)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Set up notification threshold based on message size. We're doing
|
||||
* this to try and avoid draining feature as much as possible. Whenever
|
||||
* we have big messages to transfer (bigger than our total fifo size)
|
||||
* then we might use draining feature to transfer the remaining bytes.
|
||||
*/
|
||||
|
||||
dev->threshold = clamp(size, (u8) 1, dev->fifo_size);
|
||||
|
||||
buf = omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG);
|
||||
|
||||
if (is_rx) {
|
||||
/* Clear RX Threshold */
|
||||
buf &= ~(0x3f << 8);
|
||||
buf |= ((dev->threshold - 1) << 8) | OMAP_I2C_BUF_RXFIF_CLR;
|
||||
} else {
|
||||
/* Clear TX Threshold */
|
||||
buf &= ~0x3f;
|
||||
buf |= (dev->threshold - 1) | OMAP_I2C_BUF_TXFIF_CLR;
|
||||
}
|
||||
|
||||
omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, buf);
|
||||
|
||||
if (dev->rev < OMAP_I2C_REV_ON_3630_4430)
|
||||
dev->b_hw = 1; /* Enable hardware fixes */
|
||||
|
||||
/* calculate wakeup latency constraint for MPU */
|
||||
dev->latency = (1000000 * dev->threshold) / (1000 * dev->speed / 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Low level master read/write transaction.
|
||||
*/
|
||||
|
@ -477,6 +513,9 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
|
|||
if (msg->len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
dev->receiver = !!(msg->flags & I2C_M_RD);
|
||||
omap_i2c_resize_fifo(dev, msg->len, dev->receiver);
|
||||
|
||||
omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr);
|
||||
|
||||
/* REVISIT: Could the STB bit of I2C_CON be used with probing? */
|
||||
|
@ -590,8 +629,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
if (dev->set_mpu_wkup_lat != NULL)
|
||||
dev->set_mpu_wkup_lat(dev->dev, dev->latency);
|
||||
/*
|
||||
* When waiting for completion of a i2c transfer, we need to
|
||||
* set a wake up latency constraint for the MPU. This is to
|
||||
* ensure quick enough wakeup from idle, when transfer
|
||||
* completes.
|
||||
*/
|
||||
if (dev->latency)
|
||||
pm_qos_add_request(&dev->pm_qos_request,
|
||||
PM_QOS_CPU_DMA_LATENCY,
|
||||
dev->latency);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
|
||||
|
@ -599,15 +646,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
break;
|
||||
}
|
||||
|
||||
if (dev->set_mpu_wkup_lat != NULL)
|
||||
dev->set_mpu_wkup_lat(dev->dev, -1);
|
||||
if (dev->latency)
|
||||
pm_qos_remove_request(&dev->pm_qos_request);
|
||||
|
||||
if (r == 0)
|
||||
r = num;
|
||||
|
||||
omap_i2c_wait_for_bb(dev);
|
||||
out:
|
||||
pm_runtime_put(dev->dev);
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -725,186 +773,252 @@ omap_i2c_omap1_isr(int this_irq, void *dev_id)
|
|||
* data to DATA_REG. Otherwise some data bytes can be lost while transferring
|
||||
* them from the memory to the I2C interface.
|
||||
*/
|
||||
static int errata_omap3_i462(struct omap_i2c_dev *dev, u16 *stat, int *err)
|
||||
static int errata_omap3_i462(struct omap_i2c_dev *dev)
|
||||
{
|
||||
unsigned long timeout = 10000;
|
||||
u16 stat;
|
||||
|
||||
while (--timeout && !(*stat & OMAP_I2C_STAT_XUDF)) {
|
||||
if (*stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
|
||||
omap_i2c_ack_stat(dev, *stat & (OMAP_I2C_STAT_XRDY |
|
||||
do {
|
||||
stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
|
||||
if (stat & OMAP_I2C_STAT_XUDF)
|
||||
break;
|
||||
|
||||
if (stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
|
||||
omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_XRDY |
|
||||
OMAP_I2C_STAT_XDR));
|
||||
return -ETIMEDOUT;
|
||||
if (stat & OMAP_I2C_STAT_NACK) {
|
||||
dev->cmd_err |= OMAP_I2C_STAT_NACK;
|
||||
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
|
||||
}
|
||||
|
||||
if (stat & OMAP_I2C_STAT_AL) {
|
||||
dev_err(dev->dev, "Arbitration lost\n");
|
||||
dev->cmd_err |= OMAP_I2C_STAT_AL;
|
||||
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
cpu_relax();
|
||||
*stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
|
||||
}
|
||||
} while (--timeout);
|
||||
|
||||
if (!timeout) {
|
||||
dev_err(dev->dev, "timeout waiting on XUDF bit\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*err |= OMAP_I2C_STAT_XUDF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_i2c_receive_data(struct omap_i2c_dev *dev, u8 num_bytes,
|
||||
bool is_rdr)
|
||||
{
|
||||
u16 w;
|
||||
|
||||
while (num_bytes--) {
|
||||
w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
|
||||
*dev->buf++ = w;
|
||||
dev->buf_len--;
|
||||
|
||||
/*
|
||||
* Data reg in 2430, omap3 and
|
||||
* omap4 is 8 bit wide
|
||||
*/
|
||||
if (dev->flags & OMAP_I2C_FLAG_16BIT_DATA_REG) {
|
||||
*dev->buf++ = w >> 8;
|
||||
dev->buf_len--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int omap_i2c_transmit_data(struct omap_i2c_dev *dev, u8 num_bytes,
|
||||
bool is_xdr)
|
||||
{
|
||||
u16 w;
|
||||
|
||||
while (num_bytes--) {
|
||||
w = *dev->buf++;
|
||||
dev->buf_len--;
|
||||
|
||||
/*
|
||||
* Data reg in 2430, omap3 and
|
||||
* omap4 is 8 bit wide
|
||||
*/
|
||||
if (dev->flags & OMAP_I2C_FLAG_16BIT_DATA_REG) {
|
||||
w |= *dev->buf++ << 8;
|
||||
dev->buf_len--;
|
||||
}
|
||||
|
||||
if (dev->errata & I2C_OMAP_ERRATA_I462) {
|
||||
int ret;
|
||||
|
||||
ret = errata_omap3_i462(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
omap_i2c_isr(int this_irq, void *dev_id)
|
||||
omap_i2c_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct omap_i2c_dev *dev = dev_id;
|
||||
irqreturn_t ret = IRQ_HANDLED;
|
||||
u16 mask;
|
||||
u16 stat;
|
||||
|
||||
spin_lock(&dev->lock);
|
||||
mask = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
|
||||
stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
|
||||
|
||||
if (stat & mask)
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
|
||||
spin_unlock(&dev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
omap_i2c_isr_thread(int this_irq, void *dev_id)
|
||||
{
|
||||
struct omap_i2c_dev *dev = dev_id;
|
||||
unsigned long flags;
|
||||
u16 bits;
|
||||
u16 stat, w;
|
||||
int err, count = 0;
|
||||
u16 stat;
|
||||
int err = 0, count = 0;
|
||||
|
||||
if (pm_runtime_suspended(dev->dev))
|
||||
return IRQ_NONE;
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
do {
|
||||
bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
|
||||
stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
|
||||
stat &= bits;
|
||||
|
||||
/* If we're in receiver mode, ignore XDR/XRDY */
|
||||
if (dev->receiver)
|
||||
stat &= ~(OMAP_I2C_STAT_XDR | OMAP_I2C_STAT_XRDY);
|
||||
else
|
||||
stat &= ~(OMAP_I2C_STAT_RDR | OMAP_I2C_STAT_RRDY);
|
||||
|
||||
if (!stat) {
|
||||
/* my work here is done */
|
||||
goto out;
|
||||
}
|
||||
|
||||
bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
|
||||
while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
|
||||
dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
|
||||
if (count++ == 100) {
|
||||
dev_warn(dev->dev, "Too much work in one IRQ\n");
|
||||
break;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
complete:
|
||||
/*
|
||||
* Ack the stat in one go, but [R/X]DR and [R/X]RDY should be
|
||||
* acked after the data operation is complete.
|
||||
* Ref: TRM SWPU114Q Figure 18-31
|
||||
*/
|
||||
omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat &
|
||||
~(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
|
||||
OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
|
||||
|
||||
if (stat & OMAP_I2C_STAT_NACK)
|
||||
if (stat & OMAP_I2C_STAT_NACK) {
|
||||
err |= OMAP_I2C_STAT_NACK;
|
||||
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
|
||||
break;
|
||||
}
|
||||
|
||||
if (stat & OMAP_I2C_STAT_AL) {
|
||||
dev_err(dev->dev, "Arbitration lost\n");
|
||||
err |= OMAP_I2C_STAT_AL;
|
||||
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* ProDB0017052: Clear ARDY bit twice
|
||||
*/
|
||||
if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
|
||||
OMAP_I2C_STAT_AL)) {
|
||||
omap_i2c_ack_stat(dev, stat &
|
||||
(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
|
||||
OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR |
|
||||
OMAP_I2C_STAT_ARDY));
|
||||
omap_i2c_complete_cmd(dev, err);
|
||||
return IRQ_HANDLED;
|
||||
omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_RRDY |
|
||||
OMAP_I2C_STAT_RDR |
|
||||
OMAP_I2C_STAT_XRDY |
|
||||
OMAP_I2C_STAT_XDR |
|
||||
OMAP_I2C_STAT_ARDY));
|
||||
break;
|
||||
}
|
||||
if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) {
|
||||
|
||||
if (stat & OMAP_I2C_STAT_RDR) {
|
||||
u8 num_bytes = 1;
|
||||
|
||||
if (dev->fifo_size)
|
||||
num_bytes = dev->buf_len;
|
||||
|
||||
omap_i2c_receive_data(dev, num_bytes, true);
|
||||
|
||||
if (dev->errata & I2C_OMAP_ERRATA_I207)
|
||||
i2c_omap_errata_i207(dev, stat);
|
||||
|
||||
if (dev->fifo_size) {
|
||||
if (stat & OMAP_I2C_STAT_RRDY)
|
||||
num_bytes = dev->fifo_size;
|
||||
else /* read RXSTAT on RDR interrupt */
|
||||
num_bytes = (omap_i2c_read_reg(dev,
|
||||
OMAP_I2C_BUFSTAT_REG)
|
||||
>> 8) & 0x3F;
|
||||
}
|
||||
while (num_bytes) {
|
||||
num_bytes--;
|
||||
w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
|
||||
if (dev->buf_len) {
|
||||
*dev->buf++ = w;
|
||||
dev->buf_len--;
|
||||
/*
|
||||
* Data reg in 2430, omap3 and
|
||||
* omap4 is 8 bit wide
|
||||
*/
|
||||
if (dev->flags &
|
||||
OMAP_I2C_FLAG_16BIT_DATA_REG) {
|
||||
if (dev->buf_len) {
|
||||
*dev->buf++ = w >> 8;
|
||||
dev->buf_len--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (stat & OMAP_I2C_STAT_RRDY)
|
||||
dev_err(dev->dev,
|
||||
"RRDY IRQ while no data"
|
||||
" requested\n");
|
||||
if (stat & OMAP_I2C_STAT_RDR)
|
||||
dev_err(dev->dev,
|
||||
"RDR IRQ while no data"
|
||||
" requested\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
omap_i2c_ack_stat(dev,
|
||||
stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR));
|
||||
continue;
|
||||
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR);
|
||||
break;
|
||||
}
|
||||
if (stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)) {
|
||||
|
||||
if (stat & OMAP_I2C_STAT_RRDY) {
|
||||
u8 num_bytes = 1;
|
||||
if (dev->fifo_size) {
|
||||
if (stat & OMAP_I2C_STAT_XRDY)
|
||||
num_bytes = dev->fifo_size;
|
||||
else /* read TXSTAT on XDR interrupt */
|
||||
num_bytes = omap_i2c_read_reg(dev,
|
||||
OMAP_I2C_BUFSTAT_REG)
|
||||
& 0x3F;
|
||||
}
|
||||
while (num_bytes) {
|
||||
num_bytes--;
|
||||
w = 0;
|
||||
if (dev->buf_len) {
|
||||
w = *dev->buf++;
|
||||
dev->buf_len--;
|
||||
/*
|
||||
* Data reg in 2430, omap3 and
|
||||
* omap4 is 8 bit wide
|
||||
*/
|
||||
if (dev->flags &
|
||||
OMAP_I2C_FLAG_16BIT_DATA_REG) {
|
||||
if (dev->buf_len) {
|
||||
w |= *dev->buf++ << 8;
|
||||
dev->buf_len--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (stat & OMAP_I2C_STAT_XRDY)
|
||||
dev_err(dev->dev,
|
||||
"XRDY IRQ while no "
|
||||
"data to send\n");
|
||||
if (stat & OMAP_I2C_STAT_XDR)
|
||||
dev_err(dev->dev,
|
||||
"XDR IRQ while no "
|
||||
"data to send\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if ((dev->errata & I2C_OMAP_ERRATA_I462) &&
|
||||
errata_omap3_i462(dev, &stat, &err))
|
||||
goto complete;
|
||||
if (dev->threshold)
|
||||
num_bytes = dev->threshold;
|
||||
|
||||
omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
|
||||
}
|
||||
omap_i2c_ack_stat(dev,
|
||||
stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
|
||||
omap_i2c_receive_data(dev, num_bytes, false);
|
||||
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RRDY);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stat & OMAP_I2C_STAT_XDR) {
|
||||
u8 num_bytes = 1;
|
||||
int ret;
|
||||
|
||||
if (dev->fifo_size)
|
||||
num_bytes = dev->buf_len;
|
||||
|
||||
ret = omap_i2c_transmit_data(dev, num_bytes, true);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XDR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (stat & OMAP_I2C_STAT_XRDY) {
|
||||
u8 num_bytes = 1;
|
||||
int ret;
|
||||
|
||||
if (dev->threshold)
|
||||
num_bytes = dev->threshold;
|
||||
|
||||
ret = omap_i2c_transmit_data(dev, num_bytes, false);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XRDY);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stat & OMAP_I2C_STAT_ROVR) {
|
||||
dev_err(dev->dev, "Receive overrun\n");
|
||||
dev->cmd_err |= OMAP_I2C_STAT_ROVR;
|
||||
err |= OMAP_I2C_STAT_ROVR;
|
||||
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_ROVR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (stat & OMAP_I2C_STAT_XUDF) {
|
||||
dev_err(dev->dev, "Transmit underflow\n");
|
||||
dev->cmd_err |= OMAP_I2C_STAT_XUDF;
|
||||
err |= OMAP_I2C_STAT_XUDF;
|
||||
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XUDF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (stat);
|
||||
|
||||
return count ? IRQ_HANDLED : IRQ_NONE;
|
||||
omap_i2c_complete_cmd(dev, err);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm omap_i2c_algo = {
|
||||
|
@ -943,12 +1057,12 @@ omap_i2c_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct omap_i2c_dev *dev;
|
||||
struct i2c_adapter *adap;
|
||||
struct resource *mem, *irq, *ioarea;
|
||||
struct resource *mem;
|
||||
const struct omap_i2c_bus_platform_data *pdata =
|
||||
pdev->dev.platform_data;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
const struct of_device_id *match;
|
||||
irq_handler_t isr;
|
||||
int irq;
|
||||
int r;
|
||||
|
||||
/* NOTE: driver uses the static register mapping */
|
||||
|
@ -957,23 +1071,23 @@ omap_i2c_probe(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "no mem resource?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!irq) {
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return -ENODEV;
|
||||
return irq;
|
||||
}
|
||||
|
||||
ioarea = request_mem_region(mem->start, resource_size(mem),
|
||||
pdev->name);
|
||||
if (!ioarea) {
|
||||
dev_err(&pdev->dev, "I2C region already claimed\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(struct omap_i2c_dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
r = -ENOMEM;
|
||||
goto err_release_region;
|
||||
dev_err(&pdev->dev, "Menory allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev->base = devm_request_and_ioremap(&pdev->dev, mem);
|
||||
if (!dev->base) {
|
||||
dev_err(&pdev->dev, "I2C region already claimed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
match = of_match_device(of_match_ptr(omap_i2c_of_match), &pdev->dev);
|
||||
|
@ -990,17 +1104,13 @@ omap_i2c_probe(struct platform_device *pdev)
|
|||
} else if (pdata != NULL) {
|
||||
dev->speed = pdata->clkrate;
|
||||
dev->flags = pdata->flags;
|
||||
dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
|
||||
dev->dtrev = pdata->rev;
|
||||
}
|
||||
|
||||
dev->dev = &pdev->dev;
|
||||
dev->irq = irq->start;
|
||||
dev->base = ioremap(mem->start, resource_size(mem));
|
||||
if (!dev->base) {
|
||||
r = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
dev->irq = irq;
|
||||
|
||||
spin_lock_init(&dev->lock);
|
||||
|
||||
platform_set_drvdata(pdev, dev);
|
||||
init_completion(&dev->cmd_complete);
|
||||
|
@ -1013,6 +1123,9 @@ omap_i2c_probe(struct platform_device *pdev)
|
|||
dev->regs = (u8 *)reg_map_ip_v1;
|
||||
|
||||
pm_runtime_enable(dev->dev);
|
||||
pm_runtime_set_autosuspend_delay(dev->dev, OMAP_I2C_PM_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(dev->dev);
|
||||
|
||||
r = pm_runtime_get_sync(dev->dev);
|
||||
if (IS_ERR_VALUE(r))
|
||||
goto err_free_mem;
|
||||
|
@ -1042,32 +1155,31 @@ omap_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
dev->fifo_size = (dev->fifo_size / 2);
|
||||
|
||||
if (dev->rev >= OMAP_I2C_REV_ON_3630_4430)
|
||||
dev->b_hw = 0; /* Disable hardware fixes */
|
||||
else
|
||||
if (dev->rev < OMAP_I2C_REV_ON_3630_4430)
|
||||
dev->b_hw = 1; /* Enable hardware fixes */
|
||||
|
||||
/* calculate wakeup latency constraint for MPU */
|
||||
if (dev->set_mpu_wkup_lat != NULL)
|
||||
dev->latency = (1000000 * dev->fifo_size) /
|
||||
(1000 * dev->speed / 8);
|
||||
dev->latency = (1000000 * dev->fifo_size) /
|
||||
(1000 * dev->speed / 8);
|
||||
}
|
||||
|
||||
/* reset ASAP, clearing any IRQs */
|
||||
omap_i2c_init(dev);
|
||||
|
||||
isr = (dev->rev < OMAP_I2C_OMAP1_REV_2) ? omap_i2c_omap1_isr :
|
||||
omap_i2c_isr;
|
||||
r = request_irq(dev->irq, isr, IRQF_NO_SUSPEND, pdev->name, dev);
|
||||
if (dev->rev < OMAP_I2C_OMAP1_REV_2)
|
||||
r = devm_request_irq(&pdev->dev, dev->irq, omap_i2c_omap1_isr,
|
||||
IRQF_NO_SUSPEND, pdev->name, dev);
|
||||
else
|
||||
r = devm_request_threaded_irq(&pdev->dev, dev->irq,
|
||||
omap_i2c_isr, omap_i2c_isr_thread,
|
||||
IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
pdev->name, dev);
|
||||
|
||||
if (r) {
|
||||
dev_err(dev->dev, "failure requesting irq %i\n", dev->irq);
|
||||
goto err_unuse_clocks;
|
||||
}
|
||||
|
||||
dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", pdev->id,
|
||||
dev->dtrev, dev->rev >> 4, dev->rev & 0xf, dev->speed);
|
||||
|
||||
adap = &dev->adapter;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
adap->owner = THIS_MODULE;
|
||||
|
@ -1082,27 +1194,25 @@ omap_i2c_probe(struct platform_device *pdev)
|
|||
r = i2c_add_numbered_adapter(adap);
|
||||
if (r) {
|
||||
dev_err(dev->dev, "failure adding adapter\n");
|
||||
goto err_free_irq;
|
||||
goto err_unuse_clocks;
|
||||
}
|
||||
|
||||
dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", adap->nr,
|
||||
dev->dtrev, dev->rev >> 4, dev->rev & 0xf, dev->speed);
|
||||
|
||||
of_i2c_register_devices(adap);
|
||||
|
||||
pm_runtime_put(dev->dev);
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(dev->irq, dev);
|
||||
err_unuse_clocks:
|
||||
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
|
||||
pm_runtime_put(dev->dev);
|
||||
iounmap(dev->base);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
err_free_mem:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(dev);
|
||||
err_release_region:
|
||||
release_mem_region(mem->start, resource_size(mem));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -1110,12 +1220,10 @@ err_release_region:
|
|||
static int __devexit omap_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_i2c_dev *dev = platform_get_drvdata(pdev);
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
free_irq(dev->irq, dev);
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (IS_ERR_VALUE(ret))
|
||||
|
@ -1124,10 +1232,6 @@ static int __devexit omap_i2c_remove(struct platform_device *pdev)
|
|||
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
iounmap(dev->base);
|
||||
kfree(dev);
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(mem->start, resource_size(mem));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,709 @@
|
|||
/*
|
||||
* drivers/i2c/busses/i2c-rcar.c
|
||||
*
|
||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This file is based on the drivers/i2c/busses/i2c-sh7760.c
|
||||
* (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com>
|
||||
*
|
||||
* This file used out-of-tree driver i2c-rcar.c
|
||||
* Copyright (C) 2011-2012 Renesas Electronics Corporation
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/i2c-rcar.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* register offsets */
|
||||
#define ICSCR 0x00 /* slave ctrl */
|
||||
#define ICMCR 0x04 /* master ctrl */
|
||||
#define ICSSR 0x08 /* slave status */
|
||||
#define ICMSR 0x0C /* master status */
|
||||
#define ICSIER 0x10 /* slave irq enable */
|
||||
#define ICMIER 0x14 /* master irq enable */
|
||||
#define ICCCR 0x18 /* clock dividers */
|
||||
#define ICSAR 0x1C /* slave address */
|
||||
#define ICMAR 0x20 /* master address */
|
||||
#define ICRXTX 0x24 /* data port */
|
||||
|
||||
/* ICMCR */
|
||||
#define MDBS (1 << 7) /* non-fifo mode switch */
|
||||
#define FSCL (1 << 6) /* override SCL pin */
|
||||
#define FSDA (1 << 5) /* override SDA pin */
|
||||
#define OBPC (1 << 4) /* override pins */
|
||||
#define MIE (1 << 3) /* master if enable */
|
||||
#define TSBE (1 << 2)
|
||||
#define FSB (1 << 1) /* force stop bit */
|
||||
#define ESG (1 << 0) /* en startbit gen */
|
||||
|
||||
/* ICMSR */
|
||||
#define MNR (1 << 6) /* nack received */
|
||||
#define MAL (1 << 5) /* arbitration lost */
|
||||
#define MST (1 << 4) /* sent a stop */
|
||||
#define MDE (1 << 3)
|
||||
#define MDT (1 << 2)
|
||||
#define MDR (1 << 1)
|
||||
#define MAT (1 << 0) /* slave addr xfer done */
|
||||
|
||||
/* ICMIE */
|
||||
#define MNRE (1 << 6) /* nack irq en */
|
||||
#define MALE (1 << 5) /* arblos irq en */
|
||||
#define MSTE (1 << 4) /* stop irq en */
|
||||
#define MDEE (1 << 3)
|
||||
#define MDTE (1 << 2)
|
||||
#define MDRE (1 << 1)
|
||||
#define MATE (1 << 0) /* address sent irq en */
|
||||
|
||||
|
||||
enum {
|
||||
RCAR_BUS_PHASE_ADDR,
|
||||
RCAR_BUS_PHASE_DATA,
|
||||
RCAR_BUS_PHASE_STOP,
|
||||
};
|
||||
|
||||
enum {
|
||||
RCAR_IRQ_CLOSE,
|
||||
RCAR_IRQ_OPEN_FOR_SEND,
|
||||
RCAR_IRQ_OPEN_FOR_RECV,
|
||||
RCAR_IRQ_OPEN_FOR_STOP,
|
||||
};
|
||||
|
||||
/*
|
||||
* flags
|
||||
*/
|
||||
#define ID_LAST_MSG (1 << 0)
|
||||
#define ID_IOERROR (1 << 1)
|
||||
#define ID_DONE (1 << 2)
|
||||
#define ID_ARBLOST (1 << 3)
|
||||
#define ID_NACK (1 << 4)
|
||||
|
||||
struct rcar_i2c_priv {
|
||||
void __iomem *io;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_msg *msg;
|
||||
|
||||
spinlock_t lock;
|
||||
wait_queue_head_t wait;
|
||||
|
||||
int pos;
|
||||
int irq;
|
||||
u32 icccr;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
|
||||
#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
|
||||
|
||||
#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
|
||||
#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
|
||||
|
||||
#define LOOP_TIMEOUT 1024
|
||||
|
||||
/*
|
||||
* basic functions
|
||||
*/
|
||||
static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
|
||||
{
|
||||
writel(val, priv->io + reg);
|
||||
}
|
||||
|
||||
static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
|
||||
{
|
||||
return readl(priv->io + reg);
|
||||
}
|
||||
|
||||
static void rcar_i2c_init(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
/*
|
||||
* reset slave mode.
|
||||
* slave mode is not used on this driver
|
||||
*/
|
||||
rcar_i2c_write(priv, ICSIER, 0);
|
||||
rcar_i2c_write(priv, ICSAR, 0);
|
||||
rcar_i2c_write(priv, ICSCR, 0);
|
||||
rcar_i2c_write(priv, ICSSR, 0);
|
||||
|
||||
/* reset master mode */
|
||||
rcar_i2c_write(priv, ICMIER, 0);
|
||||
rcar_i2c_write(priv, ICMCR, 0);
|
||||
rcar_i2c_write(priv, ICMSR, 0);
|
||||
rcar_i2c_write(priv, ICMAR, 0);
|
||||
}
|
||||
|
||||
static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
|
||||
{
|
||||
u32 val = MNRE | MALE | MSTE | MATE; /* default */
|
||||
|
||||
switch (open) {
|
||||
case RCAR_IRQ_OPEN_FOR_SEND:
|
||||
val |= MDEE; /* default + send */
|
||||
break;
|
||||
case RCAR_IRQ_OPEN_FOR_RECV:
|
||||
val |= MDRE; /* default + read */
|
||||
break;
|
||||
case RCAR_IRQ_OPEN_FOR_STOP:
|
||||
val = MSTE; /* stop irq only */
|
||||
break;
|
||||
case RCAR_IRQ_CLOSE:
|
||||
default:
|
||||
val = 0; /* all close */
|
||||
break;
|
||||
}
|
||||
rcar_i2c_write(priv, ICMIER, val);
|
||||
}
|
||||
|
||||
static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
|
||||
{
|
||||
rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
|
||||
}
|
||||
|
||||
/*
|
||||
* bus control functions
|
||||
*/
|
||||
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LOOP_TIMEOUT; i++) {
|
||||
/* make sure that bus is not busy */
|
||||
if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
|
||||
{
|
||||
switch (phase) {
|
||||
case RCAR_BUS_PHASE_ADDR:
|
||||
rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
|
||||
break;
|
||||
case RCAR_BUS_PHASE_DATA:
|
||||
rcar_i2c_write(priv, ICMCR, MDBS | MIE);
|
||||
break;
|
||||
case RCAR_BUS_PHASE_STOP:
|
||||
rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* clock function
|
||||
*/
|
||||
static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
|
||||
u32 bus_speed,
|
||||
struct device *dev)
|
||||
{
|
||||
struct clk *clkp = clk_get(NULL, "peripheral_clk");
|
||||
u32 scgd, cdf;
|
||||
u32 round, ick;
|
||||
u32 scl;
|
||||
|
||||
if (!clkp) {
|
||||
dev_err(dev, "there is no peripheral_clk\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* calculate SCL clock
|
||||
* see
|
||||
* ICCCR
|
||||
*
|
||||
* ick = clkp / (1 + CDF)
|
||||
* SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
|
||||
*
|
||||
* ick : I2C internal clock < 20 MHz
|
||||
* ticf : I2C SCL falling time = 35 ns here
|
||||
* tr : I2C SCL rising time = 200 ns here
|
||||
* intd : LSI internal delay = 50 ns here
|
||||
* clkp : peripheral_clk
|
||||
* F[] : integer up-valuation
|
||||
*/
|
||||
for (cdf = 0; cdf < 4; cdf++) {
|
||||
ick = clk_get_rate(clkp) / (1 + cdf);
|
||||
if (ick < 20000000)
|
||||
goto ick_find;
|
||||
}
|
||||
dev_err(dev, "there is no best CDF\n");
|
||||
return -EIO;
|
||||
|
||||
ick_find:
|
||||
/*
|
||||
* it is impossible to calculate large scale
|
||||
* number on u32. separate it
|
||||
*
|
||||
* F[(ticf + tr + intd) * ick]
|
||||
* = F[(35 + 200 + 50)ns * ick]
|
||||
* = F[285 * ick / 1000000000]
|
||||
* = F[(ick / 1000000) * 285 / 1000]
|
||||
*/
|
||||
round = (ick + 500000) / 1000000 * 285;
|
||||
round = (round + 500) / 1000;
|
||||
|
||||
/*
|
||||
* SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
|
||||
*
|
||||
* Calculation result (= SCL) should be less than
|
||||
* bus_speed for hardware safety
|
||||
*/
|
||||
for (scgd = 0; scgd < 0x40; scgd++) {
|
||||
scl = ick / (20 + (scgd * 8) + round);
|
||||
if (scl <= bus_speed)
|
||||
goto scgd_find;
|
||||
}
|
||||
dev_err(dev, "it is impossible to calculate best SCL\n");
|
||||
return -EIO;
|
||||
|
||||
scgd_find:
|
||||
dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
|
||||
scl, bus_speed, clk_get_rate(clkp), round, cdf, scgd);
|
||||
|
||||
/*
|
||||
* keep icccr value
|
||||
*/
|
||||
priv->icccr = (scgd << 2 | cdf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
rcar_i2c_write(priv, ICCCR, priv->icccr);
|
||||
}
|
||||
|
||||
/*
|
||||
* status functions
|
||||
*/
|
||||
static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
return rcar_i2c_read(priv, ICMSR);
|
||||
}
|
||||
|
||||
#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
|
||||
static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
|
||||
{
|
||||
rcar_i2c_write(priv, ICMSR, ~bit);
|
||||
}
|
||||
|
||||
/*
|
||||
* recv/send functions
|
||||
*/
|
||||
static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
rcar_i2c_set_addr(priv, 1);
|
||||
rcar_i2c_status_clear(priv);
|
||||
rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
|
||||
rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_i2c_send(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* It should check bus status when send case
|
||||
*/
|
||||
ret = rcar_i2c_bus_barrier(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
rcar_i2c_set_addr(priv, 0);
|
||||
rcar_i2c_status_clear(priv);
|
||||
rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
|
||||
rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
|
||||
#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
|
||||
|
||||
/*
|
||||
* interrupt functions
|
||||
*/
|
||||
static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
||||
{
|
||||
struct i2c_msg *msg = priv->msg;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
* sometimes, unknown interrupt happened.
|
||||
* Do nothing
|
||||
*/
|
||||
if (!(msr & MDE))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If address transfer phase finished,
|
||||
* goto data phase.
|
||||
*/
|
||||
if (msr & MAT)
|
||||
rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
|
||||
|
||||
if (priv->pos < msg->len) {
|
||||
/*
|
||||
* Prepare next data to ICRXTX register.
|
||||
* This data will go to _SHIFT_ register.
|
||||
*
|
||||
* *
|
||||
* [ICRXTX] -> [SHIFT] -> [I2C bus]
|
||||
*/
|
||||
rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
|
||||
priv->pos++;
|
||||
|
||||
} else {
|
||||
/*
|
||||
* The last data was pushed to ICRXTX on _PREV_ empty irq.
|
||||
* It is on _SHIFT_ register, and will sent to I2C bus.
|
||||
*
|
||||
* *
|
||||
* [ICRXTX] -> [SHIFT] -> [I2C bus]
|
||||
*/
|
||||
|
||||
if (priv->flags & ID_LAST_MSG)
|
||||
/*
|
||||
* If current msg is the _LAST_ msg,
|
||||
* prepare stop condition here.
|
||||
* ID_DONE will be set on STOP irq.
|
||||
*/
|
||||
rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
|
||||
else
|
||||
/*
|
||||
* If current msg is _NOT_ last msg,
|
||||
* it doesn't call stop phase.
|
||||
* thus, there is no STOP irq.
|
||||
* return ID_DONE here.
|
||||
*/
|
||||
return ID_DONE;
|
||||
}
|
||||
|
||||
rcar_i2c_send_restart(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
|
||||
{
|
||||
struct i2c_msg *msg = priv->msg;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
* sometimes, unknown interrupt happened.
|
||||
* Do nothing
|
||||
*/
|
||||
if (!(msr & MDR))
|
||||
return 0;
|
||||
|
||||
if (msr & MAT) {
|
||||
/*
|
||||
* Address transfer phase finished,
|
||||
* but, there is no data at this point.
|
||||
* Do nothing.
|
||||
*/
|
||||
} else if (priv->pos < msg->len) {
|
||||
/*
|
||||
* get received data
|
||||
*/
|
||||
msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
|
||||
priv->pos++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If next received data is the _LAST_,
|
||||
* go to STOP phase,
|
||||
* otherwise, go to DATA phase.
|
||||
*/
|
||||
if (priv->pos + 1 >= msg->len)
|
||||
rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
|
||||
else
|
||||
rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
|
||||
|
||||
rcar_i2c_recv_restart(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = ptr;
|
||||
struct device *dev = rcar_i2c_priv_to_dev(priv);
|
||||
u32 msr;
|
||||
|
||||
/*-------------- spin lock -----------------*/
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
msr = rcar_i2c_status_get(priv);
|
||||
|
||||
/*
|
||||
* Arbitration lost
|
||||
*/
|
||||
if (msr & MAL) {
|
||||
/*
|
||||
* CAUTION
|
||||
*
|
||||
* When arbitration lost, device become _slave_ mode.
|
||||
*/
|
||||
dev_dbg(dev, "Arbitration Lost\n");
|
||||
rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop
|
||||
*/
|
||||
if (msr & MST) {
|
||||
dev_dbg(dev, "Stop\n");
|
||||
rcar_i2c_flags_set(priv, ID_DONE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nack
|
||||
*/
|
||||
if (msr & MNR) {
|
||||
dev_dbg(dev, "Nack\n");
|
||||
|
||||
/* go to stop phase */
|
||||
rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
|
||||
rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
|
||||
rcar_i2c_flags_set(priv, ID_NACK);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* recv/send
|
||||
*/
|
||||
if (rcar_i2c_is_recv(priv))
|
||||
rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
|
||||
else
|
||||
rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
|
||||
|
||||
out:
|
||||
if (rcar_i2c_flags_has(priv, ID_DONE)) {
|
||||
rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
|
||||
rcar_i2c_status_clear(priv);
|
||||
wake_up(&priv->wait);
|
||||
}
|
||||
|
||||
spin_unlock(&priv->lock);
|
||||
/*-------------- spin unlock -----------------*/
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
struct device *dev = rcar_i2c_priv_to_dev(priv);
|
||||
unsigned long flags;
|
||||
int i, ret, timeout;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
/*-------------- spin lock -----------------*/
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
rcar_i2c_init(priv);
|
||||
rcar_i2c_clock_start(priv);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
/*-------------- spin unlock -----------------*/
|
||||
|
||||
ret = -EINVAL;
|
||||
for (i = 0; i < num; i++) {
|
||||
/*-------------- spin lock -----------------*/
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
/* init each data */
|
||||
priv->msg = &msgs[i];
|
||||
priv->pos = 0;
|
||||
priv->flags = 0;
|
||||
if (priv->msg == &msgs[num - 1])
|
||||
rcar_i2c_flags_set(priv, ID_LAST_MSG);
|
||||
|
||||
/* start send/recv */
|
||||
if (rcar_i2c_is_recv(priv))
|
||||
ret = rcar_i2c_recv(priv);
|
||||
else
|
||||
ret = rcar_i2c_send(priv);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
/*-------------- spin unlock -----------------*/
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
* wait result
|
||||
*/
|
||||
timeout = wait_event_timeout(priv->wait,
|
||||
rcar_i2c_flags_has(priv, ID_DONE),
|
||||
5 * HZ);
|
||||
if (!timeout) {
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* error handling
|
||||
*/
|
||||
if (rcar_i2c_flags_has(priv, ID_NACK)) {
|
||||
ret = -EREMOTEIO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = i + 1; /* The number of transfer */
|
||||
}
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(dev, "error %d : %x\n", ret, priv->flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 rcar_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm rcar_i2c_algo = {
|
||||
.master_xfer = rcar_i2c_master_xfer,
|
||||
.functionality = rcar_i2c_func,
|
||||
};
|
||||
|
||||
static int __devinit rcar_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct rcar_i2c_priv *priv;
|
||||
struct i2c_adapter *adap;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
u32 bus_speed;
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "no mmio resources\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(dev, "no mem for private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bus_speed = 100000; /* default 100 kHz */
|
||||
if (pdata && pdata->bus_speed)
|
||||
bus_speed = pdata->bus_speed;
|
||||
ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
priv->io = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!priv->io) {
|
||||
dev_err(dev, "cannot ioremap\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
init_waitqueue_head(&priv->wait);
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
adap = &priv->adap;
|
||||
adap->nr = pdev->id;
|
||||
adap->algo = &rcar_i2c_algo;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
adap->retries = 3;
|
||||
adap->dev.parent = dev;
|
||||
i2c_set_adapdata(adap, priv);
|
||||
strlcpy(adap->name, pdev->name, sizeof(adap->name));
|
||||
|
||||
ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0,
|
||||
dev_name(dev), priv);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "cannot get irq %d\n", priv->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_add_numbered_adapter(adap);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "reg adap failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
dev_info(dev, "probed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit rcar_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
i2c_del_adapter(&priv->adap);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rcar_i2c_drv = {
|
||||
.driver = {
|
||||
.name = "i2c-rcar",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = rcar_i2c_probe,
|
||||
.remove = __devexit_p(rcar_i2c_remove),
|
||||
};
|
||||
|
||||
module_platform_driver(rcar_i2c_drv);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
|
||||
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
|
|
@ -601,14 +601,14 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
|
|||
int ret;
|
||||
|
||||
pm_runtime_get_sync(&adap->dev);
|
||||
clk_enable(i2c->clk);
|
||||
clk_prepare_enable(i2c->clk);
|
||||
|
||||
for (retry = 0; retry < adap->retries; retry++) {
|
||||
|
||||
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
|
||||
|
||||
if (ret != -EAGAIN) {
|
||||
clk_disable(i2c->clk);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
pm_runtime_put(&adap->dev);
|
||||
return ret;
|
||||
}
|
||||
|
@ -618,7 +618,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
|
|||
udelay(100);
|
||||
}
|
||||
|
||||
clk_disable(i2c->clk);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
pm_runtime_put(&adap->dev);
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
@ -977,7 +977,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
|
||||
|
||||
clk_enable(i2c->clk);
|
||||
clk_prepare_enable(i2c->clk);
|
||||
|
||||
/* map the registers */
|
||||
|
||||
|
@ -1065,7 +1065,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
|||
pm_runtime_enable(&i2c->adap.dev);
|
||||
|
||||
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
|
||||
clk_disable(i2c->clk);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
return 0;
|
||||
|
||||
err_cpufreq:
|
||||
|
@ -1082,7 +1082,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
|||
kfree(i2c->ioarea);
|
||||
|
||||
err_clk:
|
||||
clk_disable(i2c->clk);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
clk_put(i2c->clk);
|
||||
|
||||
err_noclk:
|
||||
|
@ -1106,7 +1106,7 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
|
|||
i2c_del_adapter(&i2c->adap);
|
||||
free_irq(i2c->irq, i2c);
|
||||
|
||||
clk_disable(i2c->clk);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
clk_put(i2c->clk);
|
||||
|
||||
iounmap(i2c->regs);
|
||||
|
@ -1135,9 +1135,9 @@ static int s3c24xx_i2c_resume(struct device *dev)
|
|||
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
i2c->suspended = 0;
|
||||
clk_enable(i2c->clk);
|
||||
clk_prepare_enable(i2c->clk);
|
||||
s3c24xx_i2c_init(i2c);
|
||||
clk_disable(i2c->clk);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ struct i2c_algo_pca_data {
|
|||
* 330000, 288000, 217000, 146000, 88000, 59000, 44000, 36000
|
||||
* For PCA9665, use the frequency you want here. */
|
||||
unsigned int i2c_clock;
|
||||
unsigned int chip;
|
||||
};
|
||||
|
||||
int i2c_pca_add_bus(struct i2c_adapter *);
|
||||
|
|
|
@ -34,7 +34,6 @@ struct omap_i2c_bus_platform_data {
|
|||
u32 clkrate;
|
||||
u32 rev;
|
||||
u32 flags;
|
||||
void (*set_mpu_wkup_lat)(struct device *dev, long set);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef __I2C_R_CAR_H__
|
||||
#define __I2C_R_CAR_H__
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct i2c_rcar_platform_data {
|
||||
u32 bus_speed;
|
||||
};
|
||||
|
||||
#endif /* __I2C_R_CAR_H__ */
|
|
@ -28,7 +28,7 @@ enum i2c_freq_mode {
|
|||
* @sm: speed mode
|
||||
*/
|
||||
struct nmk_i2c_controller {
|
||||
unsigned long clk_freq;
|
||||
u32 clk_freq;
|
||||
unsigned short slsu;
|
||||
unsigned char tft;
|
||||
unsigned char rft;
|
||||
|
|
Загрузка…
Ссылка в новой задаче