mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 09:19:51 +00:00
Merge branch 'u300' into devel
This commit is contained in:
commit
59b69e27ca
16 changed files with 1056 additions and 72 deletions
|
@ -81,6 +81,18 @@ config MACH_U300_SEMI_IS_SHARED
|
|||
Memory Interface) from both from access and application
|
||||
side.
|
||||
|
||||
config MACH_U300_SPIDUMMY
|
||||
bool "SSP/SPI dummy chip"
|
||||
select SPI
|
||||
select SPI_MASTER
|
||||
select SPI_PL022
|
||||
help
|
||||
This creates a small kernel module that creates a dummy
|
||||
SPI device to be used for loopback tests. Regularly used
|
||||
to test reference designs. If you're not testing SPI,
|
||||
you don't need it. Selecting this will activate the
|
||||
SPI framework and ARM PL022 support.
|
||||
|
||||
comment "All the settings below must match the bootloader's settings"
|
||||
|
||||
config MACH_U300_ACCESS_MEM_SIZE
|
||||
|
|
|
@ -9,3 +9,6 @@ obj- :=
|
|||
|
||||
obj-$(CONFIG_ARCH_U300) += u300.o
|
||||
obj-$(CONFIG_MMC) += mmc.o
|
||||
obj-$(CONFIG_SPI_PL022) += spi.o
|
||||
obj-$(CONFIG_MACH_U300_SPIDUMMY) += dummyspichip.o
|
||||
obj-$(CONFIG_I2C_STU300) += i2c.o
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include "clock.h"
|
||||
#include "mmc.h"
|
||||
#include "spi.h"
|
||||
#include "i2c.h"
|
||||
|
||||
/*
|
||||
* Static I/O mappings that are needed for booting the U300 platforms. The
|
||||
|
@ -378,14 +380,14 @@ static struct platform_device wdog_device = {
|
|||
};
|
||||
|
||||
static struct platform_device i2c0_device = {
|
||||
.name = "stddci2c",
|
||||
.name = "stu300",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(i2c0_resources),
|
||||
.resource = i2c0_resources,
|
||||
};
|
||||
|
||||
static struct platform_device i2c1_device = {
|
||||
.name = "stddci2c",
|
||||
.name = "stu300",
|
||||
.id = 1,
|
||||
.num_resources = ARRAY_SIZE(i2c1_resources),
|
||||
.resource = i2c1_resources,
|
||||
|
@ -611,6 +613,8 @@ void __init u300_init_devices(void)
|
|||
/* Wait for the PLL208 to lock if not locked in yet */
|
||||
while (!(readw(U300_SYSCON_VBASE + U300_SYSCON_CSR) &
|
||||
U300_SYSCON_CSR_PLL208_LOCK_IND));
|
||||
/* Initialize SPI device with some board specifics */
|
||||
u300_spi_init(&pl022_device);
|
||||
|
||||
/* Register the AMBA devices in the AMBA bus abstraction layer */
|
||||
u300_clock_primecells();
|
||||
|
@ -622,6 +626,12 @@ void __init u300_init_devices(void)
|
|||
|
||||
u300_assign_physmem();
|
||||
|
||||
/* Register subdevices on the I2C buses */
|
||||
u300_i2c_register_board_devices();
|
||||
|
||||
/* Register subdevices on the SPI bus */
|
||||
u300_spi_register_board_devices();
|
||||
|
||||
/* Register the platform devices */
|
||||
platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs));
|
||||
|
||||
|
|
290
arch/arm/mach-u300/dummyspichip.c
Normal file
290
arch/arm/mach-u300/dummyspichip.c
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* arch/arm/mach-u300/dummyspichip.c
|
||||
*
|
||||
* Copyright (C) 2007-2009 ST-Ericsson AB
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* This is a dummy loopback SPI "chip" used for testing SPI.
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
/*
|
||||
* WARNING! Do not include this pl022-specific controller header
|
||||
* for any generic driver. It is only done in this dummy chip
|
||||
* because we alter the chip configuration in order to test some
|
||||
* different settings on the loopback device. Normal chip configs
|
||||
* shall be STATIC and not altered by the driver!
|
||||
*/
|
||||
#include <linux/amba/pl022.h>
|
||||
|
||||
struct dummy {
|
||||
struct device *dev;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
#define DMA_TEST_SIZE 2048
|
||||
|
||||
/* When we cat /sys/bus/spi/devices/spi0.0/looptest this will be triggered */
|
||||
static ssize_t dummy_looptest(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct dummy *p_dummy = dev_get_drvdata(&spi->dev);
|
||||
|
||||
/*
|
||||
* WARNING! Do not dereference the chip-specific data in any normal
|
||||
* driver for a chip. It is usually STATIC and shall not be read
|
||||
* or written to. Your chip driver should NOT depend on fields in this
|
||||
* struct, this is just used here to alter the behaviour of the chip
|
||||
* in order to perform tests.
|
||||
*/
|
||||
struct pl022_config_chip *chip_info = spi->controller_data;
|
||||
int status;
|
||||
u8 txbuf[14] = {0xDE, 0xAD, 0xBE, 0xEF, 0x2B, 0xAD,
|
||||
0xCA, 0xFE, 0xBA, 0xBE, 0xB1, 0x05,
|
||||
0xF0, 0x0D};
|
||||
u8 rxbuf[14];
|
||||
u8 *bigtxbuf_virtual;
|
||||
u8 *bigrxbuf_virtual;
|
||||
|
||||
if (mutex_lock_interruptible(&p_dummy->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
bigtxbuf_virtual = kmalloc(DMA_TEST_SIZE, GFP_KERNEL);
|
||||
if (bigtxbuf_virtual == NULL) {
|
||||
status = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
bigrxbuf_virtual = kmalloc(DMA_TEST_SIZE, GFP_KERNEL);
|
||||
|
||||
/* Fill TXBUF with some happy pattern */
|
||||
memset(bigtxbuf_virtual, 0xAA, DMA_TEST_SIZE);
|
||||
|
||||
/*
|
||||
* Force chip to 8 bit mode
|
||||
* WARNING: NEVER DO THIS IN REAL DRIVER CODE, THIS SHOULD BE STATIC!
|
||||
*/
|
||||
chip_info->data_size = SSP_DATA_BITS_8;
|
||||
/* You should NOT DO THIS EITHER */
|
||||
spi->master->setup(spi);
|
||||
|
||||
/* Now run the tests for 8bit mode */
|
||||
pr_info("Simple test 1: write 0xAA byte, read back garbage byte "
|
||||
"in 8bit mode\n");
|
||||
status = spi_w8r8(spi, 0xAA);
|
||||
if (status < 0)
|
||||
pr_warning("Siple test 1: FAILURE: spi_write_then_read "
|
||||
"failed with status %d\n", status);
|
||||
else
|
||||
pr_info("Simple test 1: SUCCESS!\n");
|
||||
|
||||
pr_info("Simple test 2: write 8 bytes, read back 8 bytes garbage "
|
||||
"in 8bit mode (full FIFO)\n");
|
||||
status = spi_write_then_read(spi, &txbuf[0], 8, &rxbuf[0], 8);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 2: FAILURE: spi_write_then_read() "
|
||||
"failed with status %d\n", status);
|
||||
else
|
||||
pr_info("Simple test 2: SUCCESS!\n");
|
||||
|
||||
pr_info("Simple test 3: write 14 bytes, read back 14 bytes garbage "
|
||||
"in 8bit mode (see if we overflow FIFO)\n");
|
||||
status = spi_write_then_read(spi, &txbuf[0], 14, &rxbuf[0], 14);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 3: FAILURE: failed with status %d "
|
||||
"(probably FIFO overrun)\n", status);
|
||||
else
|
||||
pr_info("Simple test 3: SUCCESS!\n");
|
||||
|
||||
pr_info("Simple test 4: write 8 bytes with spi_write(), read 8 "
|
||||
"bytes garbage with spi_read() in 8bit mode\n");
|
||||
status = spi_write(spi, &txbuf[0], 8);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 4 step 1: FAILURE: spi_write() "
|
||||
"failed with status %d\n", status);
|
||||
else
|
||||
pr_info("Simple test 4 step 1: SUCCESS!\n");
|
||||
status = spi_read(spi, &rxbuf[0], 8);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 4 step 2: FAILURE: spi_read() "
|
||||
"failed with status %d\n", status);
|
||||
else
|
||||
pr_info("Simple test 4 step 2: SUCCESS!\n");
|
||||
|
||||
pr_info("Simple test 5: write 14 bytes with spi_write(), read "
|
||||
"14 bytes garbage with spi_read() in 8bit mode\n");
|
||||
status = spi_write(spi, &txbuf[0], 14);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 5 step 1: FAILURE: spi_write() "
|
||||
"failed with status %d (probably FIFO overrun)\n",
|
||||
status);
|
||||
else
|
||||
pr_info("Simple test 5 step 1: SUCCESS!\n");
|
||||
status = spi_read(spi, &rxbuf[0], 14);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 5 step 2: FAILURE: spi_read() "
|
||||
"failed with status %d (probably FIFO overrun)\n",
|
||||
status);
|
||||
else
|
||||
pr_info("Simple test 5: SUCCESS!\n");
|
||||
|
||||
pr_info("Simple test 6: write %d bytes with spi_write(), "
|
||||
"read %d bytes garbage with spi_read() in 8bit mode\n",
|
||||
DMA_TEST_SIZE, DMA_TEST_SIZE);
|
||||
status = spi_write(spi, &bigtxbuf_virtual[0], DMA_TEST_SIZE);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 6 step 1: FAILURE: spi_write() "
|
||||
"failed with status %d (probably FIFO overrun)\n",
|
||||
status);
|
||||
else
|
||||
pr_info("Simple test 6 step 1: SUCCESS!\n");
|
||||
status = spi_read(spi, &bigrxbuf_virtual[0], DMA_TEST_SIZE);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 6 step 2: FAILURE: spi_read() "
|
||||
"failed with status %d (probably FIFO overrun)\n",
|
||||
status);
|
||||
else
|
||||
pr_info("Simple test 6: SUCCESS!\n");
|
||||
|
||||
|
||||
/*
|
||||
* Force chip to 16 bit mode
|
||||
* WARNING: NEVER DO THIS IN REAL DRIVER CODE, THIS SHOULD BE STATIC!
|
||||
*/
|
||||
chip_info->data_size = SSP_DATA_BITS_16;
|
||||
/* You should NOT DO THIS EITHER */
|
||||
spi->master->setup(spi);
|
||||
|
||||
pr_info("Simple test 7: write 0xAA byte, read back garbage byte "
|
||||
"in 16bit bus mode\n");
|
||||
status = spi_w8r8(spi, 0xAA);
|
||||
if (status == -EIO)
|
||||
pr_info("Simple test 7: SUCCESS! (expected failure with "
|
||||
"status EIO)\n");
|
||||
else if (status < 0)
|
||||
pr_warning("Siple test 7: FAILURE: spi_write_then_read "
|
||||
"failed with status %d\n", status);
|
||||
else
|
||||
pr_warning("Siple test 7: FAILURE: spi_write_then_read "
|
||||
"succeeded but it was expected to fail!\n");
|
||||
|
||||
pr_info("Simple test 8: write 8 bytes, read back 8 bytes garbage "
|
||||
"in 16bit mode (full FIFO)\n");
|
||||
status = spi_write_then_read(spi, &txbuf[0], 8, &rxbuf[0], 8);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 8: FAILURE: spi_write_then_read() "
|
||||
"failed with status %d\n", status);
|
||||
else
|
||||
pr_info("Simple test 8: SUCCESS!\n");
|
||||
|
||||
pr_info("Simple test 9: write 14 bytes, read back 14 bytes garbage "
|
||||
"in 16bit mode (see if we overflow FIFO)\n");
|
||||
status = spi_write_then_read(spi, &txbuf[0], 14, &rxbuf[0], 14);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 9: FAILURE: failed with status %d "
|
||||
"(probably FIFO overrun)\n", status);
|
||||
else
|
||||
pr_info("Simple test 9: SUCCESS!\n");
|
||||
|
||||
pr_info("Simple test 10: write %d bytes with spi_write(), "
|
||||
"read %d bytes garbage with spi_read() in 16bit mode\n",
|
||||
DMA_TEST_SIZE, DMA_TEST_SIZE);
|
||||
status = spi_write(spi, &bigtxbuf_virtual[0], DMA_TEST_SIZE);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 10 step 1: FAILURE: spi_write() "
|
||||
"failed with status %d (probably FIFO overrun)\n",
|
||||
status);
|
||||
else
|
||||
pr_info("Simple test 10 step 1: SUCCESS!\n");
|
||||
|
||||
status = spi_read(spi, &bigrxbuf_virtual[0], DMA_TEST_SIZE);
|
||||
if (status < 0)
|
||||
pr_warning("Simple test 10 step 2: FAILURE: spi_read() "
|
||||
"failed with status %d (probably FIFO overrun)\n",
|
||||
status);
|
||||
else
|
||||
pr_info("Simple test 10: SUCCESS!\n");
|
||||
|
||||
status = sprintf(buf, "loop test complete\n");
|
||||
kfree(bigrxbuf_virtual);
|
||||
kfree(bigtxbuf_virtual);
|
||||
out:
|
||||
mutex_unlock(&p_dummy->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(looptest, S_IRUGO, dummy_looptest, NULL);
|
||||
|
||||
static int __devinit pl022_dummy_probe(struct spi_device *spi)
|
||||
{
|
||||
struct dummy *p_dummy;
|
||||
int status;
|
||||
|
||||
dev_info(&spi->dev, "probing dummy SPI device\n");
|
||||
|
||||
p_dummy = kzalloc(sizeof *p_dummy, GFP_KERNEL);
|
||||
if (!p_dummy)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, p_dummy);
|
||||
mutex_init(&p_dummy->lock);
|
||||
|
||||
/* sysfs hook */
|
||||
status = device_create_file(&spi->dev, &dev_attr_looptest);
|
||||
if (status) {
|
||||
dev_dbg(&spi->dev, "device_create_file looptest failure.\n");
|
||||
goto out_dev_create_looptest_failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_dev_create_looptest_failed:
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(p_dummy);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __devexit pl022_dummy_remove(struct spi_device *spi)
|
||||
{
|
||||
struct dummy *p_dummy = dev_get_drvdata(&spi->dev);
|
||||
|
||||
dev_info(&spi->dev, "removing dummy SPI device\n");
|
||||
device_remove_file(&spi->dev, &dev_attr_looptest);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(p_dummy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver pl022_dummy_driver = {
|
||||
.driver = {
|
||||
.name = "spi-dummy",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = pl022_dummy_probe,
|
||||
.remove = __devexit_p(pl022_dummy_remove),
|
||||
};
|
||||
|
||||
static int __init pl022_init_dummy(void)
|
||||
{
|
||||
return spi_register_driver(&pl022_dummy_driver);
|
||||
}
|
||||
|
||||
static void __exit pl022_exit_dummy(void)
|
||||
{
|
||||
spi_unregister_driver(&pl022_dummy_driver);
|
||||
}
|
||||
|
||||
module_init(pl022_init_dummy);
|
||||
module_exit(pl022_exit_dummy);
|
||||
|
||||
MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
|
||||
MODULE_DESCRIPTION("PL022 SSP/SPI DUMMY Linux driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -25,11 +25,6 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
/* Need access to SYSCON registers for PADmuxing */
|
||||
#include <mach/syscon.h>
|
||||
|
||||
#include "padmux.h"
|
||||
|
||||
/* Reference to GPIO block clock */
|
||||
static struct clk *clk;
|
||||
|
||||
|
@ -606,14 +601,6 @@ static int __init gpio_probe(struct platform_device *pdev)
|
|||
writel(U300_GPIO_CR_BLOCK_CLKRQ_ENABLE, virtbase + U300_GPIO_CR);
|
||||
#endif
|
||||
|
||||
/* Set up some padmuxing here */
|
||||
#ifdef CONFIG_MMC
|
||||
pmx_set_mission_mode_mmc();
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_PL022
|
||||
pmx_set_mission_mode_spi();
|
||||
#endif
|
||||
|
||||
gpio_set_initial_values();
|
||||
|
||||
for (num_irqs = 0 ; num_irqs < U300_GPIO_NUM_PORTS; num_irqs++) {
|
||||
|
|
43
arch/arm/mach-u300/i2c.c
Normal file
43
arch/arm/mach-u300/i2c.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* arch/arm/mach-u300/i2c.c
|
||||
*
|
||||
* Copyright (C) 2009 ST-Ericsson AB
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*
|
||||
* Register board i2c devices
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <mach/irqs.h>
|
||||
|
||||
static struct i2c_board_info __initdata bus0_i2c_board_info[] = {
|
||||
{
|
||||
.type = "ab3100",
|
||||
.addr = 0x48,
|
||||
.irq = IRQ_U300_IRQ0_EXT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata bus1_i2c_board_info[] = {
|
||||
#ifdef CONFIG_MACH_U300_BS335
|
||||
{
|
||||
.type = "fwcam",
|
||||
.addr = 0x10,
|
||||
},
|
||||
{
|
||||
.type = "fwcam",
|
||||
.addr = 0x5d,
|
||||
},
|
||||
#else
|
||||
{ },
|
||||
#endif
|
||||
};
|
||||
|
||||
void __init u300_i2c_register_board_devices(void)
|
||||
{
|
||||
i2c_register_board_info(0, bus0_i2c_board_info,
|
||||
ARRAY_SIZE(bus0_i2c_board_info));
|
||||
i2c_register_board_info(1, bus1_i2c_board_info,
|
||||
ARRAY_SIZE(bus1_i2c_board_info));
|
||||
}
|
23
arch/arm/mach-u300/i2c.h
Normal file
23
arch/arm/mach-u300/i2c.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* arch/arm/mach-u300/i2c.h
|
||||
*
|
||||
* Copyright (C) 2009 ST-Ericsson AB
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*
|
||||
* Register board i2c devices
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
*/
|
||||
|
||||
#ifndef MACH_U300_I2C_H
|
||||
#define MACH_U300_I2C_H
|
||||
|
||||
#ifdef CONFIG_I2C_STU300
|
||||
void __init u300_i2c_register_board_devices(void);
|
||||
#else
|
||||
/* Compile out this stuff if no I2C adapter is available */
|
||||
static inline void __init u300_i2c_register_board_devices(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -240,8 +240,13 @@
|
|||
#define U300_SYSCON_PMC1LR_CDI_MASK (0xC000)
|
||||
#define U300_SYSCON_PMC1LR_CDI_CDI (0x0000)
|
||||
#define U300_SYSCON_PMC1LR_CDI_EMIF (0x4000)
|
||||
#ifdef CONFIG_MACH_U300_BS335
|
||||
#define U300_SYSCON_PMC1LR_CDI_CDI2 (0x8000)
|
||||
#define U300_SYSCON_PMC1LR_CDI_WCDMA_APP_GPIO (0xC000)
|
||||
#elif CONFIG_MACH_U300_BS365
|
||||
#define U300_SYSCON_PMC1LR_CDI_GPIO (0x8000)
|
||||
#define U300_SYSCON_PMC1LR_CDI_WCDMA (0xC000)
|
||||
#endif
|
||||
#define U300_SYSCON_PMC1LR_PDI_MASK (0x3000)
|
||||
#define U300_SYSCON_PMC1LR_PDI_PDI (0x0000)
|
||||
#define U300_SYSCON_PMC1LR_PDI_EGG (0x1000)
|
||||
|
@ -345,19 +350,69 @@
|
|||
#define U300_SYSCON_MMCR_MASK (0x0003)
|
||||
#define U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE (0x0002)
|
||||
#define U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE (0x0001)
|
||||
|
||||
/* Pull up/down control (R/W) */
|
||||
#define U300_SYSCON_PUCR (0x104)
|
||||
#define U300_SYSCON_PUCR_EMIF_1_WAIT_N_PU_ENABLE (0x0200)
|
||||
#define U300_SYSCON_PUCR_EMIF_1_NFIF_READY_PU_ENABLE (0x0100)
|
||||
#define U300_SYSCON_PUCR_EMIF_1_16BIT_PU_ENABLE (0x0080)
|
||||
#define U300_SYSCON_PUCR_EMIF_1_8BIT_PU_ENABLE (0x0040)
|
||||
#define U300_SYSCON_PUCR_KEY_IN_PU_EN_MASK (0x003F)
|
||||
/* Padmux 2 control */
|
||||
#define U300_SYSCON_PMC2R (0x100)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_0_MASK (0x00C0)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_0_APP_GPIO (0x0000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_0_EMIF_SDRAM (0x0040)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_0_MMC (0x0080)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_0_CDI2 (0x00C0)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_1_MASK (0x0300)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_1_APP_GPIO (0x0000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_1_EMIF_SDRAM (0x0100)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_1_MMC (0x0200)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_1_CDI2 (0x0300)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_2_MASK (0x0C00)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_2_APP_GPIO (0x0000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_2_EMIF_SDRAM (0x0400)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_2_MMC (0x0800)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_2_CDI2 (0x0C00)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_3_MASK (0x3000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_3_APP_GPIO (0x0000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_3_EMIF_SDRAM (0x1000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_3_MMC (0x2000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_3_CDI2 (0x3000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_4_MASK (0xC000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_4_APP_GPIO (0x0000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_4_EMIF_SDRAM (0x4000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_4_MMC (0x8000)
|
||||
#define U300_SYSCON_PMC2R_APP_MISC_4_ACC_GPIO (0xC000)
|
||||
/* TODO: More SYSCON registers missing */
|
||||
#define U300_SYSCON_PMC3R (0x10c)
|
||||
#define U300_SYSCON_PMC3R_APP_MISC_11_MASK (0xc000)
|
||||
#define U300_SYSCON_PMC3R_APP_MISC_11_SPI (0x4000)
|
||||
#define U300_SYSCON_PMC3R_APP_MISC_10_MASK (0x3000)
|
||||
#define U300_SYSCON_PMC3R_APP_MISC_10_SPI (0x1000)
|
||||
/* TODO: Missing other configs, I just added the SPI stuff */
|
||||
|
||||
/* TODO: Missing other configs */
|
||||
#define U300_SYSCON_PMC4R (0x168)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_12_MASK (0x0003)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_12_APP_GPIO (0x0000)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_13_MASK (0x000C)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_13_CDI (0x0000)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_13_SMIA (0x0004)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_13_SMIA2 (0x0008)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_13_APP_GPIO (0x000C)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_14_MASK (0x0030)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_14_CDI (0x0000)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_14_SMIA (0x0010)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_14_CDI2 (0x0020)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_14_APP_GPIO (0x0030)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_16_MASK (0x0300)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_16_APP_GPIO_13 (0x0000)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_16_APP_UART1_CTS (0x0100)
|
||||
#define U300_SYSCON_PMC4R_APP_MISC_16_EMIF_1_STATIC_CS5_N (0x0200)
|
||||
/* SYS_0_CLK_CONTROL first clock control 16bit (R/W) */
|
||||
#define U300_SYSCON_S0CCR (0x120)
|
||||
#define U300_SYSCON_S0CCR_FIELD_MASK (0x43FF)
|
||||
#define U300_SYSCON_S0CCR_CLOCK_REQ (0x4000)
|
||||
#define U300_SYSCON_S0CCR_CLOCK_REQ_MONITOR (0x2000)
|
||||
#define U300_SYSCON_S0CCR_CLOCK_INV (0x0200)
|
||||
#define U300_SYSCON_S0CCR_CLOCK_FREQ_MASK (0x01E0)
|
||||
#define U300_SYSCON_S0CCR_CLOCK_SELECT_MASK (0x001E)
|
||||
|
@ -375,6 +430,7 @@
|
|||
#define U300_SYSCON_S1CCR (0x124)
|
||||
#define U300_SYSCON_S1CCR_FIELD_MASK (0x43FF)
|
||||
#define U300_SYSCON_S1CCR_CLOCK_REQ (0x4000)
|
||||
#define U300_SYSCON_S1CCR_CLOCK_REQ_MONITOR (0x2000)
|
||||
#define U300_SYSCON_S1CCR_CLOCK_INV (0x0200)
|
||||
#define U300_SYSCON_S1CCR_CLOCK_FREQ_MASK (0x01E0)
|
||||
#define U300_SYSCON_S1CCR_CLOCK_SELECT_MASK (0x001E)
|
||||
|
@ -393,6 +449,7 @@
|
|||
#define U300_SYSCON_S2CCR_FIELD_MASK (0xC3FF)
|
||||
#define U300_SYSCON_S2CCR_CLK_STEAL (0x8000)
|
||||
#define U300_SYSCON_S2CCR_CLOCK_REQ (0x4000)
|
||||
#define U300_SYSCON_S2CCR_CLOCK_REQ_MONITOR (0x2000)
|
||||
#define U300_SYSCON_S2CCR_CLOCK_INV (0x0200)
|
||||
#define U300_SYSCON_S2CCR_CLOCK_FREQ_MASK (0x01E0)
|
||||
#define U300_SYSCON_S2CCR_CLOCK_SELECT_MASK (0x001E)
|
||||
|
@ -425,6 +482,44 @@
|
|||
#define U300_SYSCON_MCR_PMGEN_CR_0_EMIF_0_SDRAM (0x000C)
|
||||
#define U300_SYSCON_MCR_PM1G_MODE_ENABLE (0x0002)
|
||||
#define U300_SYSCON_MCR_PMTG5_MODE_ENABLE (0x0001)
|
||||
/* SC_PLL_IRQ_CONTROL 16bit (R/W) */
|
||||
#define U300_SYSCON_PICR (0x0130)
|
||||
#define U300_SYSCON_PICR_MASK (0x00FF)
|
||||
#define U300_SYSCON_PICR_FORCE_PLL208_LOCK_LOW_ENABLE (0x0080)
|
||||
#define U300_SYSCON_PICR_FORCE_PLL208_LOCK_HIGH_ENABLE (0x0040)
|
||||
#define U300_SYSCON_PICR_FORCE_PLL13_LOCK_LOW_ENABLE (0x0020)
|
||||
#define U300_SYSCON_PICR_FORCE_PLL13_LOCK_HIGH_ENABLE (0x0010)
|
||||
#define U300_SYSCON_PICR_IRQMASK_PLL13_UNLOCK_ENABLE (0x0008)
|
||||
#define U300_SYSCON_PICR_IRQMASK_PLL13_LOCK_ENABLE (0x0004)
|
||||
#define U300_SYSCON_PICR_IRQMASK_PLL208_UNLOCK_ENABLE (0x0002)
|
||||
#define U300_SYSCON_PICR_IRQMASK_PLL208_LOCK_ENABLE (0x0001)
|
||||
/* SC_PLL_IRQ_STATUS 16 bit (R/-) */
|
||||
#define U300_SYSCON_PISR (0x0134)
|
||||
#define U300_SYSCON_PISR_MASK (0x000F)
|
||||
#define U300_SYSCON_PISR_PLL13_UNLOCK_IND (0x0008)
|
||||
#define U300_SYSCON_PISR_PLL13_LOCK_IND (0x0004)
|
||||
#define U300_SYSCON_PISR_PLL208_UNLOCK_IND (0x0002)
|
||||
#define U300_SYSCON_PISR_PLL208_LOCK_IND (0x0001)
|
||||
/* SC_PLL_IRQ_CLEAR 16 bit (-/W) */
|
||||
#define U300_SYSCON_PICLR (0x0138)
|
||||
#define U300_SYSCON_PICLR_MASK (0x000F)
|
||||
#define U300_SYSCON_PICLR_RWMASK (0x0000)
|
||||
#define U300_SYSCON_PICLR_PLL13_UNLOCK_SC (0x0008)
|
||||
#define U300_SYSCON_PICLR_PLL13_LOCK_SC (0x0004)
|
||||
#define U300_SYSCON_PICLR_PLL208_UNLOCK_SC (0x0002)
|
||||
#define U300_SYSCON_PICLR_PLL208_LOCK_SC (0x0001)
|
||||
/* CAMIF_CONTROL 16 bit (-/W) */
|
||||
#define U300_SYSCON_CICR (0x013C)
|
||||
#define U300_SYSCON_CICR_MASK (0x0FFF)
|
||||
#define U300_SYSCON_CICR_APP_SUBLVDS_TESTMODE_MASK (0x0F00)
|
||||
#define U300_SYSCON_CICR_APP_SUBLVDS_TESTMODE_PORT1 (0x0C00)
|
||||
#define U300_SYSCON_CICR_APP_SUBLVDS_TESTMODE_PORT0 (0x0300)
|
||||
#define U300_SYSCON_CICR_APP_SUBLVDS_RESCON_MASK (0x00F0)
|
||||
#define U300_SYSCON_CICR_APP_SUBLVDS_RESCON_PORT1 (0x00C0)
|
||||
#define U300_SYSCON_CICR_APP_SUBLVDS_RESCON_PORT0 (0x0030)
|
||||
#define U300_SYSCON_CICR_APP_SUBLVDS_PWR_DWN_N_MASK (0x000F)
|
||||
#define U300_SYSCON_CICR_APP_SUBLVDS_PWR_DWN_N_PORT1 (0x000C)
|
||||
#define U300_SYSCON_CICR_APP_SUBLVDS_PWR_DWN_N_PORT0 (0x0003)
|
||||
/* Clock activity observability register 0 */
|
||||
#define U300_SYSCON_C0OAR (0x140)
|
||||
#define U300_SYSCON_C0OAR_MASK (0xFFFF)
|
||||
|
@ -513,7 +608,7 @@
|
|||
/**
|
||||
* CPU medium frequency in MHz
|
||||
*/
|
||||
#define SYSCON_CPU_CLOCK_MEDIUM 104
|
||||
#define SYSCON_CPU_CLOCK_MEDIUM 52
|
||||
/**
|
||||
* CPU low frequency in MHz
|
||||
*/
|
||||
|
@ -527,7 +622,7 @@
|
|||
/**
|
||||
* EMIF medium frequency in MHz
|
||||
*/
|
||||
#define SYSCON_EMIF_CLOCK_MEDIUM 104
|
||||
#define SYSCON_EMIF_CLOCK_MEDIUM 52
|
||||
/**
|
||||
* EMIF low frequency in MHz
|
||||
*/
|
||||
|
@ -541,7 +636,7 @@
|
|||
/**
|
||||
* AHB medium frequency in MHz
|
||||
*/
|
||||
#define SYSCON_AHB_CLOCK_MEDIUM 52
|
||||
#define SYSCON_AHB_CLOCK_MEDIUM 26
|
||||
/**
|
||||
* AHB low frequency in MHz
|
||||
*/
|
||||
|
@ -553,6 +648,15 @@ enum syscon_busmaster {
|
|||
SYSCON_BM_VIDEO_ENC
|
||||
};
|
||||
|
||||
/* Selectr a resistor or a set of resistors */
|
||||
enum syscon_pull_up_down {
|
||||
SYSCON_PU_KEY_IN_EN,
|
||||
SYSCON_PU_EMIF_1_8_BIT_EN,
|
||||
SYSCON_PU_EMIF_1_16_BIT_EN,
|
||||
SYSCON_PU_EMIF_1_NFIF_READY_EN,
|
||||
SYSCON_PU_EMIF_1_NFIF_WAIT_N_EN,
|
||||
};
|
||||
|
||||
/*
|
||||
* Note that this array must match the order of the array "clk_reg"
|
||||
* in syscon.c
|
||||
|
@ -575,6 +679,7 @@ enum syscon_clk {
|
|||
SYSCON_CLKCONTROL_SPI,
|
||||
SYSCON_CLKCONTROL_I2S0_CORE,
|
||||
SYSCON_CLKCONTROL_I2S1_CORE,
|
||||
SYSCON_CLKCONTROL_UART1,
|
||||
SYSCON_CLKCONTROL_AAIF,
|
||||
SYSCON_CLKCONTROL_AHB,
|
||||
SYSCON_CLKCONTROL_APEX,
|
||||
|
@ -604,7 +709,8 @@ enum syscon_sysclk_mode {
|
|||
|
||||
enum syscon_sysclk_req {
|
||||
SYSCON_SYSCLKREQ_DISABLED,
|
||||
SYSCON_SYSCLKREQ_ACTIVE_LOW
|
||||
SYSCON_SYSCLKREQ_ACTIVE_LOW,
|
||||
SYSCON_SYSCLKREQ_MONITOR
|
||||
};
|
||||
|
||||
enum syscon_clk_mode {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <asm/mach/mmc.h>
|
||||
#include "mmc.h"
|
||||
#include "padmux.h"
|
||||
|
||||
struct mmci_card_event {
|
||||
struct input_dev *mmc_input;
|
||||
|
@ -146,6 +147,7 @@ int __devinit mmc_init(struct amba_device *adev)
|
|||
{
|
||||
struct mmci_card_event *mmci_card;
|
||||
struct device *mmcsd_device = &adev->dev;
|
||||
struct pmx *pmx;
|
||||
int ret = 0;
|
||||
|
||||
mmci_card = kzalloc(sizeof(struct mmci_card_event), GFP_KERNEL);
|
||||
|
@ -209,6 +211,20 @@ int __devinit mmc_init(struct amba_device *adev)
|
|||
|
||||
input_set_drvdata(mmci_card->mmc_input, mmci_card);
|
||||
|
||||
/*
|
||||
* Setup padmuxing for MMC. Since this must always be
|
||||
* compiled into the kernel, pmx is never released.
|
||||
*/
|
||||
pmx = pmx_get(mmcsd_device, U300_APP_PMX_MMC_SETTING);
|
||||
|
||||
if (IS_ERR(pmx))
|
||||
pr_warning("Could not get padmux handle\n");
|
||||
else {
|
||||
ret = pmx_activate(mmcsd_device, pmx);
|
||||
if (IS_ERR_VALUE(ret))
|
||||
pr_warning("Could not activate padmuxing\n");
|
||||
}
|
||||
|
||||
ret = gpio_register_callback(U300_GPIO_PIN_MMC_CD, mmci_callback,
|
||||
mmci_card);
|
||||
|
||||
|
|
|
@ -6,53 +6,362 @@
|
|||
* Copyright (C) 2009 ST-Ericsson AB
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* U300 PADMUX functions
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
*
|
||||
* Author: Martin Persson <martin.persson@stericsson.com>
|
||||
*/
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <mach/u300-regs.h>
|
||||
#include <mach/syscon.h>
|
||||
|
||||
#include "padmux.h"
|
||||
|
||||
/* Set the PAD MUX to route the MMC reader correctly to GPIO0. */
|
||||
void pmx_set_mission_mode_mmc(void)
|
||||
{
|
||||
u16 val;
|
||||
static DEFINE_MUTEX(pmx_mutex);
|
||||
|
||||
val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMC1LR);
|
||||
val &= ~U300_SYSCON_PMC1LR_MMCSD_MASK;
|
||||
writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMC1LR);
|
||||
val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMC1HR);
|
||||
val &= ~U300_SYSCON_PMC1HR_APP_GPIO_1_MASK;
|
||||
val |= U300_SYSCON_PMC1HR_APP_GPIO_1_MMC;
|
||||
writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMC1HR);
|
||||
const u32 pmx_registers[] = {
|
||||
(U300_SYSCON_VBASE + U300_SYSCON_PMC1LR),
|
||||
(U300_SYSCON_VBASE + U300_SYSCON_PMC1HR),
|
||||
(U300_SYSCON_VBASE + U300_SYSCON_PMC2R),
|
||||
(U300_SYSCON_VBASE + U300_SYSCON_PMC3R),
|
||||
(U300_SYSCON_VBASE + U300_SYSCON_PMC4R)
|
||||
};
|
||||
|
||||
/* High level functionality */
|
||||
|
||||
/* Lazy dog:
|
||||
* onmask = {
|
||||
* {"PMC1LR" mask, "PMC1LR" value},
|
||||
* {"PMC1HR" mask, "PMC1HR" value},
|
||||
* {"PMC2R" mask, "PMC2R" value},
|
||||
* {"PMC3R" mask, "PMC3R" value},
|
||||
* {"PMC4R" mask, "PMC4R" value}
|
||||
* }
|
||||
*/
|
||||
static struct pmx mmc_setting = {
|
||||
.setting = U300_APP_PMX_MMC_SETTING,
|
||||
.default_on = false,
|
||||
.activated = false,
|
||||
.name = "MMC",
|
||||
.onmask = {
|
||||
{U300_SYSCON_PMC1LR_MMCSD_MASK,
|
||||
U300_SYSCON_PMC1LR_MMCSD_MMCSD},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{U300_SYSCON_PMC4R_APP_MISC_12_MASK,
|
||||
U300_SYSCON_PMC4R_APP_MISC_12_APP_GPIO}
|
||||
},
|
||||
};
|
||||
|
||||
static struct pmx spi_setting = {
|
||||
.setting = U300_APP_PMX_SPI_SETTING,
|
||||
.default_on = false,
|
||||
.activated = false,
|
||||
.name = "SPI",
|
||||
.onmask = {{0, 0},
|
||||
{U300_SYSCON_PMC1HR_APP_SPI_2_MASK |
|
||||
U300_SYSCON_PMC1HR_APP_SPI_CS_1_MASK |
|
||||
U300_SYSCON_PMC1HR_APP_SPI_CS_2_MASK,
|
||||
U300_SYSCON_PMC1HR_APP_SPI_2_SPI |
|
||||
U300_SYSCON_PMC1HR_APP_SPI_CS_1_SPI |
|
||||
U300_SYSCON_PMC1HR_APP_SPI_CS_2_SPI},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{0, 0}
|
||||
},
|
||||
};
|
||||
|
||||
/* Available padmux settings */
|
||||
static struct pmx *pmx_settings[] = {
|
||||
&mmc_setting,
|
||||
&spi_setting,
|
||||
};
|
||||
|
||||
static void update_registers(struct pmx *pmx, bool activate)
|
||||
{
|
||||
u16 regval, val, mask;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmx_registers); i++) {
|
||||
if (activate)
|
||||
val = pmx->onmask[i].val;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
mask = pmx->onmask[i].mask;
|
||||
if (mask != 0) {
|
||||
regval = readw(pmx_registers[i]);
|
||||
regval &= ~mask;
|
||||
regval |= val;
|
||||
writew(regval, pmx_registers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pmx_set_mission_mode_spi(void)
|
||||
struct pmx *pmx_get(struct device *dev, enum pmx_settings setting)
|
||||
{
|
||||
u16 val;
|
||||
int i;
|
||||
struct pmx *pmx = ERR_PTR(-ENOENT);
|
||||
|
||||
/* Set up padmuxing so the SPI port and its chipselects are active */
|
||||
val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMC1HR);
|
||||
/*
|
||||
* Activate the SPI port (disable the use of these pins for generic
|
||||
* GPIO, DSP, AAIF
|
||||
*/
|
||||
val &= ~U300_SYSCON_PMC1HR_APP_SPI_2_MASK;
|
||||
val |= U300_SYSCON_PMC1HR_APP_SPI_2_SPI;
|
||||
/*
|
||||
* Use GPIO pin SPI CS1 for CS1 actually (it can be used for other
|
||||
* things also)
|
||||
*/
|
||||
val &= ~U300_SYSCON_PMC1HR_APP_SPI_CS_1_MASK;
|
||||
val |= U300_SYSCON_PMC1HR_APP_SPI_CS_1_SPI;
|
||||
/*
|
||||
* Use GPIO pin SPI CS2 for CS2 actually (it can be used for other
|
||||
* things also)
|
||||
*/
|
||||
val &= ~U300_SYSCON_PMC1HR_APP_SPI_CS_2_MASK;
|
||||
val |= U300_SYSCON_PMC1HR_APP_SPI_CS_2_SPI;
|
||||
writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMC1HR);
|
||||
if (dev == NULL)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mutex_lock(&pmx_mutex);
|
||||
for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
|
||||
|
||||
if (setting == pmx_settings[i]->setting) {
|
||||
|
||||
if (pmx_settings[i]->dev != NULL) {
|
||||
WARN(1, "padmux: required setting "
|
||||
"in use by another consumer\n");
|
||||
} else {
|
||||
pmx = pmx_settings[i];
|
||||
pmx->dev = dev;
|
||||
dev_dbg(dev, "padmux: setting nr %d is now "
|
||||
"bound to %s and ready to use\n",
|
||||
setting, dev_name(dev));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&pmx_mutex);
|
||||
|
||||
return pmx;
|
||||
}
|
||||
EXPORT_SYMBOL(pmx_get);
|
||||
|
||||
int pmx_put(struct device *dev, struct pmx *pmx)
|
||||
{
|
||||
int i;
|
||||
int ret = -ENOENT;
|
||||
|
||||
if (pmx == NULL || dev == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pmx_mutex);
|
||||
for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
|
||||
|
||||
if (pmx->setting == pmx_settings[i]->setting) {
|
||||
|
||||
if (dev != pmx->dev) {
|
||||
WARN(1, "padmux: cannot release handle as "
|
||||
"it is bound to another consumer\n");
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
} else {
|
||||
pmx_settings[i]->dev = NULL;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&pmx_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pmx_put);
|
||||
|
||||
int pmx_activate(struct device *dev, struct pmx *pmx)
|
||||
{
|
||||
int i, j, ret;
|
||||
ret = 0;
|
||||
|
||||
if (pmx == NULL || dev == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pmx_mutex);
|
||||
|
||||
/* Make sure the required bits are not used */
|
||||
for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
|
||||
|
||||
if (pmx_settings[i]->dev == NULL || pmx_settings[i] == pmx)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(pmx_registers); j++) {
|
||||
|
||||
if (pmx_settings[i]->onmask[j].mask & pmx->
|
||||
onmask[j].mask) {
|
||||
/* More than one entry on the same bits */
|
||||
WARN(1, "padmux: cannot activate "
|
||||
"setting. Bit conflict with "
|
||||
"an active setting\n");
|
||||
|
||||
ret = -EUSERS;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
update_registers(pmx, true);
|
||||
pmx->activated = true;
|
||||
dev_dbg(dev, "padmux: setting nr %d is activated\n",
|
||||
pmx->setting);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&pmx_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pmx_activate);
|
||||
|
||||
int pmx_deactivate(struct device *dev, struct pmx *pmx)
|
||||
{
|
||||
int i;
|
||||
int ret = -ENOENT;
|
||||
|
||||
if (pmx == NULL || dev == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pmx_mutex);
|
||||
for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
|
||||
|
||||
if (pmx_settings[i]->dev == NULL)
|
||||
continue;
|
||||
|
||||
if (pmx->setting == pmx_settings[i]->setting) {
|
||||
|
||||
if (dev != pmx->dev) {
|
||||
WARN(1, "padmux: cannot deactivate "
|
||||
"pmx setting as it was activated "
|
||||
"by another consumer\n");
|
||||
|
||||
ret = -EBUSY;
|
||||
continue;
|
||||
} else {
|
||||
update_registers(pmx, false);
|
||||
pmx_settings[i]->dev = NULL;
|
||||
pmx->activated = false;
|
||||
ret = 0;
|
||||
dev_dbg(dev, "padmux: setting nr %d is deactivated",
|
||||
pmx->setting);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&pmx_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pmx_deactivate);
|
||||
|
||||
/*
|
||||
* For internal use only. If it is to be exported,
|
||||
* it should be reentrant. Notice that pmx_activate
|
||||
* (i.e. runtime settings) always override default settings.
|
||||
*/
|
||||
static int pmx_set_default(void)
|
||||
{
|
||||
/* Used to identify several entries on the same bits */
|
||||
u16 modbits[ARRAY_SIZE(pmx_registers)];
|
||||
|
||||
int i, j;
|
||||
|
||||
memset(modbits, 0, ARRAY_SIZE(pmx_registers) * sizeof(u16));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
|
||||
|
||||
if (!pmx_settings[i]->default_on)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(pmx_registers); j++) {
|
||||
|
||||
/* Make sure there is only one entry on the same bits */
|
||||
if (modbits[j] & pmx_settings[i]->onmask[j].mask) {
|
||||
BUG();
|
||||
return -EUSERS;
|
||||
}
|
||||
modbits[j] |= pmx_settings[i]->onmask[j].mask;
|
||||
}
|
||||
update_registers(pmx_settings[i], true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if (defined(CONFIG_DEBUG_FS) && defined(CONFIG_U300_DEBUG))
|
||||
static int pmx_show(struct seq_file *s, void *data)
|
||||
{
|
||||
int i;
|
||||
seq_printf(s, "-------------------------------------------------\n");
|
||||
seq_printf(s, "SETTING BOUND TO DEVICE STATE\n");
|
||||
seq_printf(s, "-------------------------------------------------\n");
|
||||
mutex_lock(&pmx_mutex);
|
||||
for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
|
||||
/* Format pmx and device name nicely */
|
||||
char cdp[33];
|
||||
int chars;
|
||||
|
||||
chars = snprintf(&cdp[0], 17, "%s", pmx_settings[i]->name);
|
||||
while (chars < 16) {
|
||||
cdp[chars] = ' ';
|
||||
chars++;
|
||||
}
|
||||
chars = snprintf(&cdp[16], 17, "%s", pmx_settings[i]->dev ?
|
||||
dev_name(pmx_settings[i]->dev) : "N/A");
|
||||
while (chars < 16) {
|
||||
cdp[chars+16] = ' ';
|
||||
chars++;
|
||||
}
|
||||
cdp[32] = '\0';
|
||||
|
||||
seq_printf(s,
|
||||
"%s\t%s\n",
|
||||
&cdp[0],
|
||||
pmx_settings[i]->activated ?
|
||||
"ACTIVATED" : "DEACTIVATED"
|
||||
);
|
||||
|
||||
}
|
||||
mutex_unlock(&pmx_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmx_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, pmx_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations pmx_operations = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = pmx_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int __init init_pmx_read_debugfs(void)
|
||||
{
|
||||
/* Expose a simple debugfs interface to view pmx settings */
|
||||
(void) debugfs_create_file("padmux", S_IFREG | S_IRUGO,
|
||||
NULL, NULL,
|
||||
&pmx_operations);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This needs to come in after the core_initcall(),
|
||||
* because debugfs is not available until
|
||||
* the subsystems come up.
|
||||
*/
|
||||
module_init(init_pmx_read_debugfs);
|
||||
#endif
|
||||
|
||||
static int __init pmx_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pmx_set_default();
|
||||
|
||||
if (IS_ERR_VALUE(ret))
|
||||
pr_crit("padmux: default settings could not be set\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Should be initialized before consumers */
|
||||
core_initcall(pmx_init);
|
||||
|
|
|
@ -6,14 +6,34 @@
|
|||
* Copyright (C) 2009 ST-Ericsson AB
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* U300 PADMUX API
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
*
|
||||
* Author: Martin Persson <martin.persson@stericsson.com>
|
||||
*/
|
||||
|
||||
#ifndef __MACH_U300_PADMUX_H
|
||||
#define __MACH_U300_PADMUX_H
|
||||
|
||||
void pmx_set_mission_mode_mmc(void);
|
||||
void pmx_set_mission_mode_spi(void);
|
||||
enum pmx_settings {
|
||||
U300_APP_PMX_MMC_SETTING,
|
||||
U300_APP_PMX_SPI_SETTING
|
||||
};
|
||||
|
||||
struct pmx_onmask {
|
||||
u16 mask; /* Mask bits */
|
||||
u16 val; /* Value when active */
|
||||
};
|
||||
|
||||
struct pmx {
|
||||
struct device *dev;
|
||||
enum pmx_settings setting;
|
||||
char *name;
|
||||
bool activated;
|
||||
bool default_on;
|
||||
struct pmx_onmask onmask[];
|
||||
};
|
||||
|
||||
struct pmx *pmx_get(struct device *dev, enum pmx_settings setting);
|
||||
int pmx_put(struct device *dev, struct pmx *pmx);
|
||||
int pmx_activate(struct device *dev, struct pmx *pmx);
|
||||
int pmx_deactivate(struct device *dev, struct pmx *pmx);
|
||||
|
||||
#endif
|
||||
|
|
124
arch/arm/mach-u300/spi.c
Normal file
124
arch/arm/mach-u300/spi.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* arch/arm/mach-u300/spi.c
|
||||
*
|
||||
* Copyright (C) 2009 ST-Ericsson AB
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/amba/pl022.h>
|
||||
#include <linux/err.h>
|
||||
#include "padmux.h"
|
||||
|
||||
/*
|
||||
* The following is for the actual devices on the SSP/SPI bus
|
||||
*/
|
||||
#ifdef CONFIG_MACH_U300_SPIDUMMY
|
||||
static void select_dummy_chip(u32 chipselect)
|
||||
{
|
||||
pr_debug("CORE: %s called with CS=0x%x (%s)\n",
|
||||
__func__,
|
||||
chipselect,
|
||||
chipselect ? "unselect chip" : "select chip");
|
||||
/*
|
||||
* Here you would write the chip select value to the GPIO pins if
|
||||
* this was a real chip (but this is a loopback dummy).
|
||||
*/
|
||||
}
|
||||
|
||||
struct pl022_config_chip dummy_chip_info = {
|
||||
/* Nominally this is LOOPBACK_DISABLED, but this is our dummy chip! */
|
||||
.lbm = LOOPBACK_ENABLED,
|
||||
/*
|
||||
* available POLLING_TRANSFER and INTERRUPT_TRANSFER,
|
||||
* DMA_TRANSFER does not work
|
||||
*/
|
||||
.com_mode = INTERRUPT_TRANSFER,
|
||||
.iface = SSP_INTERFACE_MOTOROLA_SPI,
|
||||
/* We can only act as master but SSP_SLAVE is possible in theory */
|
||||
.hierarchy = SSP_MASTER,
|
||||
/* 0 = drive TX even as slave, 1 = do not drive TX as slave */
|
||||
.slave_tx_disable = 0,
|
||||
/* LSB first */
|
||||
.endian_tx = SSP_TX_LSB,
|
||||
.endian_rx = SSP_RX_LSB,
|
||||
.data_size = SSP_DATA_BITS_8, /* used to be 12 in some default */
|
||||
.rx_lev_trig = SSP_RX_1_OR_MORE_ELEM,
|
||||
.tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC,
|
||||
.clk_phase = SSP_CLK_SECOND_EDGE,
|
||||
.clk_pol = SSP_CLK_POL_IDLE_LOW,
|
||||
.ctrl_len = SSP_BITS_12,
|
||||
.wait_state = SSP_MWIRE_WAIT_ZERO,
|
||||
.duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
|
||||
/*
|
||||
* This is where you insert a call to a function to enable CS
|
||||
* (usually GPIO) for a certain chip.
|
||||
*/
|
||||
.cs_control = select_dummy_chip,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct spi_board_info u300_spi_devices[] = {
|
||||
#ifdef CONFIG_MACH_U300_SPIDUMMY
|
||||
{
|
||||
/* A dummy chip used for loopback tests */
|
||||
.modalias = "spi-dummy",
|
||||
/* Really dummy, pass in additional chip config here */
|
||||
.platform_data = NULL,
|
||||
/* This defines how the controller shall handle the device */
|
||||
.controller_data = &dummy_chip_info,
|
||||
/* .irq - no external IRQ routed from this device */
|
||||
.max_speed_hz = 1000000,
|
||||
.bus_num = 0, /* Only one bus on this chip */
|
||||
.chip_select = 0,
|
||||
/* Means SPI_CS_HIGH, change if e.g low CS */
|
||||
.mode = 0,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct pl022_ssp_controller ssp_platform_data = {
|
||||
/* If you have several SPI buses this varies, we have only bus 0 */
|
||||
.bus_id = 0,
|
||||
/* Set this to 1 when we think we got DMA working */
|
||||
.enable_dma = 0,
|
||||
/*
|
||||
* On the APP CPU GPIO 4, 5 and 6 are connected as generic
|
||||
* chip selects for SPI. (Same on U330, U335 and U365.)
|
||||
* TODO: make sure the GPIO driver can select these properly
|
||||
* and do padmuxing accordingly too.
|
||||
*/
|
||||
.num_chipselect = 3,
|
||||
};
|
||||
|
||||
|
||||
void __init u300_spi_init(struct amba_device *adev)
|
||||
{
|
||||
struct pmx *pmx;
|
||||
|
||||
adev->dev.platform_data = &ssp_platform_data;
|
||||
/*
|
||||
* Setup padmuxing for SPI. Since this must always be
|
||||
* compiled into the kernel, pmx is never released.
|
||||
*/
|
||||
pmx = pmx_get(&adev->dev, U300_APP_PMX_SPI_SETTING);
|
||||
|
||||
if (IS_ERR(pmx))
|
||||
dev_warn(&adev->dev, "Could not get padmux handle\n");
|
||||
else {
|
||||
int ret;
|
||||
|
||||
ret = pmx_activate(&adev->dev, pmx);
|
||||
if (IS_ERR_VALUE(ret))
|
||||
dev_warn(&adev->dev, "Could not activate padmuxing\n");
|
||||
}
|
||||
|
||||
}
|
||||
void __init u300_spi_register_board_devices(void)
|
||||
{
|
||||
/* Register any SPI devices */
|
||||
spi_register_board_info(u300_spi_devices, ARRAY_SIZE(u300_spi_devices));
|
||||
}
|
26
arch/arm/mach-u300/spi.h
Normal file
26
arch/arm/mach-u300/spi.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* arch/arm/mach-u300/spi.h
|
||||
*
|
||||
* Copyright (C) 2009 ST-Ericsson AB
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
*/
|
||||
#ifndef SPI_H
|
||||
#define SPI_H
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
#ifdef CONFIG_SPI_PL022
|
||||
void __init u300_spi_init(struct amba_device *adev);
|
||||
void __init u300_spi_register_board_devices(void);
|
||||
#else
|
||||
/* Compile out SPI support if PL022 is not selected */
|
||||
static inline void __init u300_spi_init(struct amba_device *adev)
|
||||
{
|
||||
}
|
||||
static inline void __init u300_spi_register_board_devices(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -346,6 +346,21 @@ static struct clocksource clocksource_u300_1mhz = {
|
|||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
};
|
||||
|
||||
/*
|
||||
* Override the global weak sched_clock symbol with this
|
||||
* local implementation which uses the clocksource to get some
|
||||
* better resolution when scheduling the kernel. We accept that
|
||||
* this wraps around for now, since it is just a relative time
|
||||
* stamp. (Inspired by OMAP implementation.)
|
||||
*/
|
||||
unsigned long long notrace sched_clock(void)
|
||||
{
|
||||
return clocksource_cyc2ns(clocksource_u300_1mhz.read(
|
||||
&clocksource_u300_1mhz),
|
||||
clocksource_u300_1mhz.mult,
|
||||
clocksource_u300_1mhz.shift);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This sets up the system timers, clock source and clock event.
|
||||
|
|
|
@ -532,7 +532,7 @@ static void restore_state(struct pl022 *pl022)
|
|||
GEN_MASK_BITS(SSP_DATA_BITS_12, SSP_CR0_MASK_DSS, 0) | \
|
||||
GEN_MASK_BITS(SSP_MICROWIRE_CHANNEL_FULL_DUPLEX, SSP_CR0_MASK_HALFDUP, 5) | \
|
||||
GEN_MASK_BITS(SSP_CLK_POL_IDLE_LOW, SSP_CR0_MASK_SPO, 6) | \
|
||||
GEN_MASK_BITS(SSP_CLK_FALLING_EDGE, SSP_CR0_MASK_SPH, 7) | \
|
||||
GEN_MASK_BITS(SSP_CLK_SECOND_EDGE, SSP_CR0_MASK_SPH, 7) | \
|
||||
GEN_MASK_BITS(NMDK_SSP_DEFAULT_CLKRATE, SSP_CR0_MASK_SCR, 8) | \
|
||||
GEN_MASK_BITS(SSP_BITS_8, SSP_CR0_MASK_CSS, 16) | \
|
||||
GEN_MASK_BITS(SSP_INTERFACE_MOTOROLA_SPI, SSP_CR0_MASK_FRF, 21) \
|
||||
|
@ -1247,8 +1247,8 @@ static int verify_controller_parameters(struct pl022 *pl022,
|
|||
return -EINVAL;
|
||||
}
|
||||
if (chip_info->iface == SSP_INTERFACE_MOTOROLA_SPI) {
|
||||
if ((chip_info->clk_phase != SSP_CLK_RISING_EDGE)
|
||||
&& (chip_info->clk_phase != SSP_CLK_FALLING_EDGE)) {
|
||||
if ((chip_info->clk_phase != SSP_CLK_FIRST_EDGE)
|
||||
&& (chip_info->clk_phase != SSP_CLK_SECOND_EDGE)) {
|
||||
dev_err(chip_info->dev,
|
||||
"Clock Phase is configured incorrectly\n");
|
||||
return -EINVAL;
|
||||
|
@ -1485,7 +1485,7 @@ static int pl022_setup(struct spi_device *spi)
|
|||
chip_info->data_size = SSP_DATA_BITS_12;
|
||||
chip_info->rx_lev_trig = SSP_RX_1_OR_MORE_ELEM;
|
||||
chip_info->tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC;
|
||||
chip_info->clk_phase = SSP_CLK_FALLING_EDGE;
|
||||
chip_info->clk_phase = SSP_CLK_SECOND_EDGE;
|
||||
chip_info->clk_pol = SSP_CLK_POL_IDLE_LOW;
|
||||
chip_info->ctrl_len = SSP_BITS_8;
|
||||
chip_info->wait_state = SSP_MWIRE_WAIT_ZERO;
|
||||
|
|
|
@ -136,12 +136,12 @@ enum ssp_tx_level_trig {
|
|||
|
||||
/**
|
||||
* enum SPI Clock Phase - clock phase (Motorola SPI interface only)
|
||||
* @SSP_CLK_RISING_EDGE: Receive data on rising edge
|
||||
* @SSP_CLK_FALLING_EDGE: Receive data on falling edge
|
||||
* @SSP_CLK_FIRST_EDGE: Receive data on first edge transition (actual direction depends on polarity)
|
||||
* @SSP_CLK_SECOND_EDGE: Receive data on second edge transition (actual direction depends on polarity)
|
||||
*/
|
||||
enum ssp_spi_clk_phase {
|
||||
SSP_CLK_RISING_EDGE,
|
||||
SSP_CLK_FALLING_EDGE
|
||||
SSP_CLK_FIRST_EDGE,
|
||||
SSP_CLK_SECOND_EDGE
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue