mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
Merge branch 'ux500-core' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson into devel-stable
This commit is contained in:
commit
cde9efef40
@ -798,6 +798,7 @@ config ARCH_U8500
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select COMMON_CLKDEV
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select ARCH_HAS_CPUFREQ
|
||||
help
|
||||
Support for ST-Ericsson's Ux500 architecture
|
||||
|
||||
|
@ -2,14 +2,16 @@
|
||||
# Makefile for the linux kernel, U8500 machine.
|
||||
#
|
||||
|
||||
obj-y := clock.o cpu.o devices.o
|
||||
obj-$(CONFIG_UX500_SOC_DB5500) += cpu-db5500.o devices-db5500.o
|
||||
obj-y := clock.o cpu.o devices.o devices-common.o
|
||||
obj-$(CONFIG_UX500_SOC_DB5500) += cpu-db5500.o dma-db5500.o
|
||||
obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o devices-db8500.o prcmu.o
|
||||
obj-$(CONFIG_MACH_U8500_MOP) += board-mop500.o board-mop500-sdi.o
|
||||
obj-$(CONFIG_MACH_U5500) += board-u5500.o
|
||||
obj-$(CONFIG_MACH_U8500_MOP) += board-mop500.o board-mop500-sdi.o \
|
||||
board-mop500-keypads.o
|
||||
obj-$(CONFIG_MACH_U5500) += board-u5500.o board-u5500-sdi.o
|
||||
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
|
||||
obj-$(CONFIG_REGULATOR_AB8500) += board-mop500-regulators.o
|
||||
obj-$(CONFIG_U5500_MODEM_IRQ) += modem_irq.o
|
||||
obj-$(CONFIG_U5500_MBOX) += mbox.o
|
||||
obj-$(CONFIG_U5500_MODEM_IRQ) += modem-irq-db5500.o
|
||||
obj-$(CONFIG_U5500_MBOX) += mbox-db5500.o
|
||||
obj-$(CONFIG_CPU_FREQ) += cpufreq.o
|
||||
|
229
arch/arm/mach-ux500/board-mop500-keypads.c
Normal file
229
arch/arm/mach-ux500/board-mop500-keypads.c
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
*
|
||||
* Keypad layouts for various boards
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/stmpe.h>
|
||||
#include <linux/mfd/tc3589x.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
|
||||
#include <plat/pincfg.h>
|
||||
#include <plat/ske.h>
|
||||
|
||||
#include <mach/devices.h>
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#include "devices-db8500.h"
|
||||
#include "board-mop500.h"
|
||||
|
||||
/* STMPE/SKE keypad use this key layout */
|
||||
static const unsigned int mop500_keymap[] = {
|
||||
KEY(2, 5, KEY_END),
|
||||
KEY(4, 1, KEY_POWER),
|
||||
KEY(3, 5, KEY_VOLUMEDOWN),
|
||||
KEY(1, 3, KEY_3),
|
||||
KEY(5, 2, KEY_RIGHT),
|
||||
KEY(5, 0, KEY_9),
|
||||
|
||||
KEY(0, 5, KEY_MENU),
|
||||
KEY(7, 6, KEY_ENTER),
|
||||
KEY(4, 5, KEY_0),
|
||||
KEY(6, 7, KEY_2),
|
||||
KEY(3, 4, KEY_UP),
|
||||
KEY(3, 3, KEY_DOWN),
|
||||
|
||||
KEY(6, 4, KEY_SEND),
|
||||
KEY(6, 2, KEY_BACK),
|
||||
KEY(4, 2, KEY_VOLUMEUP),
|
||||
KEY(5, 5, KEY_1),
|
||||
KEY(4, 3, KEY_LEFT),
|
||||
KEY(3, 2, KEY_7),
|
||||
};
|
||||
|
||||
static const struct matrix_keymap_data mop500_keymap_data = {
|
||||
.keymap = mop500_keymap,
|
||||
.keymap_size = ARRAY_SIZE(mop500_keymap),
|
||||
};
|
||||
|
||||
/*
|
||||
* Nomadik SKE keypad
|
||||
*/
|
||||
#define ROW_PIN_I0 164
|
||||
#define ROW_PIN_I1 163
|
||||
#define ROW_PIN_I2 162
|
||||
#define ROW_PIN_I3 161
|
||||
#define ROW_PIN_I4 156
|
||||
#define ROW_PIN_I5 155
|
||||
#define ROW_PIN_I6 154
|
||||
#define ROW_PIN_I7 153
|
||||
#define COL_PIN_O0 168
|
||||
#define COL_PIN_O1 167
|
||||
#define COL_PIN_O2 166
|
||||
#define COL_PIN_O3 165
|
||||
#define COL_PIN_O4 160
|
||||
#define COL_PIN_O5 159
|
||||
#define COL_PIN_O6 158
|
||||
#define COL_PIN_O7 157
|
||||
|
||||
#define SKE_KPD_MAX_ROWS 8
|
||||
#define SKE_KPD_MAX_COLS 8
|
||||
|
||||
static int ske_kp_rows[] = {
|
||||
ROW_PIN_I0, ROW_PIN_I1, ROW_PIN_I2, ROW_PIN_I3,
|
||||
ROW_PIN_I4, ROW_PIN_I5, ROW_PIN_I6, ROW_PIN_I7,
|
||||
};
|
||||
|
||||
/*
|
||||
* ske_set_gpio_row: request and set gpio rows
|
||||
*/
|
||||
static int ske_set_gpio_row(int gpio)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gpio_request(gpio, "ske-kp");
|
||||
if (ret < 0) {
|
||||
pr_err("ske_set_gpio_row: gpio request failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_direction_output(gpio, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("ske_set_gpio_row: gpio direction failed\n");
|
||||
gpio_free(gpio);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* ske_kp_init - enable the gpio configuration
|
||||
*/
|
||||
static int ske_kp_init(void)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < SKE_KPD_MAX_ROWS; i++) {
|
||||
ret = ske_set_gpio_row(ske_kp_rows[i]);
|
||||
if (ret < 0) {
|
||||
pr_err("ske_kp_init: failed init\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ske_keypad_platform_data ske_keypad_board = {
|
||||
.init = ske_kp_init,
|
||||
.keymap_data = &mop500_keymap_data,
|
||||
.no_autorepeat = true,
|
||||
.krow = SKE_KPD_MAX_ROWS, /* 8x8 matrix */
|
||||
.kcol = SKE_KPD_MAX_COLS,
|
||||
.debounce_ms = 40, /* in millisecs */
|
||||
};
|
||||
|
||||
/*
|
||||
* STMPE1601
|
||||
*/
|
||||
static struct stmpe_keypad_platform_data stmpe1601_keypad_data = {
|
||||
.debounce_ms = 64,
|
||||
.scan_count = 8,
|
||||
.no_autorepeat = true,
|
||||
.keymap_data = &mop500_keymap_data,
|
||||
};
|
||||
|
||||
static struct stmpe_platform_data stmpe1601_data = {
|
||||
.id = 1,
|
||||
.blocks = STMPE_BLOCK_KEYPAD,
|
||||
.irq_trigger = IRQF_TRIGGER_FALLING,
|
||||
.irq_base = MOP500_STMPE1601_IRQ(0),
|
||||
.keypad = &stmpe1601_keypad_data,
|
||||
.autosleep = true,
|
||||
.autosleep_timeout = 1024,
|
||||
};
|
||||
|
||||
static struct i2c_board_info mop500_i2c0_devices_stuib[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("stmpe1601", 0x40),
|
||||
.irq = NOMADIK_GPIO_TO_IRQ(218),
|
||||
.platform_data = &stmpe1601_data,
|
||||
.flags = I2C_CLIENT_WAKE,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* TC35893
|
||||
*/
|
||||
|
||||
static const unsigned int uib_keymap[] = {
|
||||
KEY(3, 1, KEY_END),
|
||||
KEY(4, 1, KEY_POWER),
|
||||
KEY(6, 4, KEY_VOLUMEDOWN),
|
||||
KEY(4, 2, KEY_EMAIL),
|
||||
KEY(3, 3, KEY_RIGHT),
|
||||
KEY(2, 5, KEY_BACKSPACE),
|
||||
|
||||
KEY(6, 7, KEY_MENU),
|
||||
KEY(5, 0, KEY_ENTER),
|
||||
KEY(4, 3, KEY_0),
|
||||
KEY(3, 4, KEY_DOT),
|
||||
KEY(5, 2, KEY_UP),
|
||||
KEY(3, 5, KEY_DOWN),
|
||||
|
||||
KEY(4, 5, KEY_SEND),
|
||||
KEY(0, 5, KEY_BACK),
|
||||
KEY(6, 2, KEY_VOLUMEUP),
|
||||
KEY(1, 3, KEY_SPACE),
|
||||
KEY(7, 6, KEY_LEFT),
|
||||
KEY(5, 5, KEY_SEARCH),
|
||||
};
|
||||
|
||||
static struct matrix_keymap_data uib_keymap_data = {
|
||||
.keymap = uib_keymap,
|
||||
.keymap_size = ARRAY_SIZE(uib_keymap),
|
||||
};
|
||||
|
||||
static struct tc3589x_keypad_platform_data tc35893_data = {
|
||||
.krow = TC_KPD_ROWS,
|
||||
.kcol = TC_KPD_COLUMNS,
|
||||
.debounce_period = TC_KPD_DEBOUNCE_PERIOD,
|
||||
.settle_time = TC_KPD_SETTLE_TIME,
|
||||
.irqtype = IRQF_TRIGGER_FALLING,
|
||||
.enable_wakeup = true,
|
||||
.keymap_data = &uib_keymap_data,
|
||||
.no_autorepeat = true,
|
||||
};
|
||||
|
||||
static struct tc3589x_platform_data tc3589x_keypad_data = {
|
||||
.block = TC3589x_BLOCK_KEYPAD,
|
||||
.keypad = &tc35893_data,
|
||||
.irq_base = MOP500_EGPIO_IRQ_BASE,
|
||||
};
|
||||
|
||||
static struct i2c_board_info mop500_i2c0_devices_uib[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("tc3589x", 0x44),
|
||||
.platform_data = &tc3589x_keypad_data,
|
||||
.irq = NOMADIK_GPIO_TO_IRQ(218),
|
||||
.flags = I2C_CLIENT_WAKE,
|
||||
},
|
||||
};
|
||||
|
||||
void mop500_keypad_init(void)
|
||||
{
|
||||
db8500_add_ske_keypad(&ske_keypad_board);
|
||||
|
||||
i2c_register_board_info(0, mop500_i2c0_devices_stuib,
|
||||
ARRAY_SIZE(mop500_i2c0_devices_stuib));
|
||||
|
||||
i2c_register_board_info(0, mop500_i2c0_devices_uib,
|
||||
ARRAY_SIZE(mop500_i2c0_devices_uib));
|
||||
|
||||
}
|
@ -16,10 +16,24 @@
|
||||
#include <mach/devices.h>
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#include "devices-db8500.h"
|
||||
#include "pins-db8500.h"
|
||||
#include "board-mop500.h"
|
||||
|
||||
static pin_cfg_t mop500_sdi_pins[] = {
|
||||
/* SDI0 (MicroSD slot) */
|
||||
GPIO18_MC0_CMDDIR,
|
||||
GPIO19_MC0_DAT0DIR,
|
||||
GPIO20_MC0_DAT2DIR,
|
||||
GPIO21_MC0_DAT31DIR,
|
||||
GPIO22_MC0_FBCLK,
|
||||
GPIO23_MC0_CLK,
|
||||
GPIO24_MC0_CMD,
|
||||
GPIO25_MC0_DAT0,
|
||||
GPIO26_MC0_DAT1,
|
||||
GPIO27_MC0_DAT2,
|
||||
GPIO28_MC0_DAT3,
|
||||
|
||||
/* SDI4 (on-board eMMC) */
|
||||
GPIO197_MC4_DAT3,
|
||||
GPIO198_MC4_DAT2,
|
||||
@ -49,6 +63,55 @@ static pin_cfg_t mop500_sdi2_pins[] = {
|
||||
GPIO138_MC2_DAT7,
|
||||
};
|
||||
|
||||
/*
|
||||
* SDI 0 (MicroSD slot)
|
||||
*/
|
||||
|
||||
/* MMCIPOWER bits */
|
||||
#define MCI_DATA2DIREN (1 << 2)
|
||||
#define MCI_CMDDIREN (1 << 3)
|
||||
#define MCI_DATA0DIREN (1 << 4)
|
||||
#define MCI_DATA31DIREN (1 << 5)
|
||||
#define MCI_FBCLKEN (1 << 7)
|
||||
|
||||
static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd,
|
||||
unsigned char power_mode)
|
||||
{
|
||||
if (power_mode == MMC_POWER_UP)
|
||||
gpio_set_value_cansleep(GPIO_SDMMC_EN, 1);
|
||||
else if (power_mode == MMC_POWER_OFF)
|
||||
gpio_set_value_cansleep(GPIO_SDMMC_EN, 0);
|
||||
|
||||
return MCI_FBCLKEN | MCI_CMDDIREN | MCI_DATA0DIREN |
|
||||
MCI_DATA2DIREN | MCI_DATA31DIREN;
|
||||
}
|
||||
|
||||
static struct mmci_platform_data mop500_sdi0_data = {
|
||||
.vdd_handler = mop500_sdi0_vdd_handler,
|
||||
.ocr_mask = MMC_VDD_29_30,
|
||||
.f_max = 100000000,
|
||||
.capabilities = MMC_CAP_4_BIT_DATA,
|
||||
.gpio_cd = GPIO_SDMMC_CD,
|
||||
.gpio_wp = -1,
|
||||
};
|
||||
|
||||
void mop500_sdi_tc35892_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gpio_request(GPIO_SDMMC_EN, "SDMMC_EN");
|
||||
if (!ret)
|
||||
ret = gpio_request(GPIO_SDMMC_1V8_3V_SEL,
|
||||
"GPIO_SDMMC_1V8_3V_SEL");
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
gpio_direction_output(GPIO_SDMMC_1V8_3V_SEL, 1);
|
||||
gpio_direction_output(GPIO_SDMMC_EN, 0);
|
||||
|
||||
db8500_add_sdi0(&mop500_sdi0_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* SDI 2 (POP eMMC, not on DB8500ed)
|
||||
*/
|
||||
@ -74,18 +137,24 @@ static struct mmci_platform_data mop500_sdi4_data = {
|
||||
.gpio_wp = -1,
|
||||
};
|
||||
|
||||
void mop500_sdi_init(void)
|
||||
void __init mop500_sdi_init(void)
|
||||
{
|
||||
nmk_config_pins(mop500_sdi_pins, ARRAY_SIZE(mop500_sdi_pins));
|
||||
|
||||
u8500_sdi2_device.dev.platform_data = &mop500_sdi2_data;
|
||||
u8500_sdi4_device.dev.platform_data = &mop500_sdi4_data;
|
||||
/*
|
||||
* sdi0 will finally be added when the TC35892 initializes and calls
|
||||
* mop500_sdi_tc35892_init() above.
|
||||
*/
|
||||
|
||||
/* PoP:ed eMMC */
|
||||
if (!cpu_is_u8500ed()) {
|
||||
nmk_config_pins(mop500_sdi2_pins, ARRAY_SIZE(mop500_sdi2_pins));
|
||||
amba_device_register(&u8500_sdi2_device, &iomem_resource);
|
||||
/* POP eMMC on v1.0 has problems with high speed */
|
||||
if (!cpu_is_u8500v10())
|
||||
mop500_sdi2_data.capabilities |= MMC_CAP_MMC_HIGHSPEED;
|
||||
db8500_add_sdi2(&mop500_sdi2_data);
|
||||
}
|
||||
|
||||
/* On-board eMMC */
|
||||
amba_device_register(&u8500_sdi4_device, &iomem_resource);
|
||||
db8500_add_sdi4(&mop500_sdi4_data);
|
||||
}
|
||||
|
@ -13,25 +13,26 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/pl022.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/mfd/tc3589x.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
|
||||
#include <plat/pincfg.h>
|
||||
#include <plat/i2c.h>
|
||||
#include <plat/ske.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/setup.h>
|
||||
#include <mach/devices.h>
|
||||
#include <mach/irqs.h>
|
||||
|
||||
#include "devices-db8500.h"
|
||||
#include "pins-db8500.h"
|
||||
#include "board-mop500.h"
|
||||
|
||||
@ -69,22 +70,12 @@ static pin_cfg_t mop500_pins[] = {
|
||||
GPIO166_KP_O2,
|
||||
GPIO167_KP_O1,
|
||||
GPIO168_KP_O0,
|
||||
};
|
||||
|
||||
static void ab4500_spi_cs_control(u32 command)
|
||||
{
|
||||
/* set the FRM signal, which is CS - TODO */
|
||||
}
|
||||
/* GPIO_EXP_INT */
|
||||
GPIO217_GPIO,
|
||||
|
||||
struct pl022_config_chip ab4500_chip_info = {
|
||||
.com_mode = INTERRUPT_TRANSFER,
|
||||
.iface = SSP_INTERFACE_MOTOROLA_SPI,
|
||||
/* we can act as master only */
|
||||
.hierarchy = SSP_MASTER,
|
||||
.slave_tx_disable = 0,
|
||||
.rx_lev_trig = SSP_RX_1_OR_MORE_ELEM,
|
||||
.tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC,
|
||||
.cs_control = ab4500_spi_cs_control,
|
||||
/* STMPE1601 IRQ */
|
||||
GPIO218_GPIO | PIN_INPUT_PULLUP,
|
||||
};
|
||||
|
||||
static struct ab8500_platform_data ab8500_platdata = {
|
||||
@ -93,9 +84,9 @@ static struct ab8500_platform_data ab8500_platdata = {
|
||||
|
||||
static struct resource ab8500_resources[] = {
|
||||
[0] = {
|
||||
.start = IRQ_AB8500,
|
||||
.end = IRQ_AB8500,
|
||||
.flags = IORESOURCE_IRQ
|
||||
.start = IRQ_DB8500_AB8500,
|
||||
.end = IRQ_DB8500_AB8500,
|
||||
.flags = IORESOURCE_IRQ
|
||||
}
|
||||
};
|
||||
|
||||
@ -109,19 +100,6 @@ struct platform_device ab8500_device = {
|
||||
.resource = ab8500_resources,
|
||||
};
|
||||
|
||||
static struct spi_board_info ab8500_spi_devices[] = {
|
||||
{
|
||||
.modalias = "ab8500-spi",
|
||||
.controller_data = &ab4500_chip_info,
|
||||
.platform_data = &ab8500_platdata,
|
||||
.max_speed_hz = 12000000,
|
||||
.bus_num = 0,
|
||||
.chip_select = 0,
|
||||
.mode = SPI_MODE_3,
|
||||
.irq = IRQ_DB8500_AB8500,
|
||||
},
|
||||
};
|
||||
|
||||
static struct pl022_ssp_controller ssp0_platform_data = {
|
||||
.bus_id = 0,
|
||||
/* pl022 not yet supports dma */
|
||||
@ -132,6 +110,34 @@ static struct pl022_ssp_controller ssp0_platform_data = {
|
||||
.num_chipselect = 5,
|
||||
};
|
||||
|
||||
/*
|
||||
* TC35892
|
||||
*/
|
||||
|
||||
static void mop500_tc35892_init(struct tc3589x *tc3589x, unsigned int base)
|
||||
{
|
||||
mop500_sdi_tc35892_init();
|
||||
}
|
||||
|
||||
static struct tc3589x_gpio_platform_data mop500_tc35892_gpio_data = {
|
||||
.gpio_base = MOP500_EGPIO(0),
|
||||
.setup = mop500_tc35892_init,
|
||||
};
|
||||
|
||||
static struct tc3589x_platform_data mop500_tc35892_data = {
|
||||
.block = TC3589x_BLOCK_GPIO,
|
||||
.gpio = &mop500_tc35892_gpio_data,
|
||||
.irq_base = MOP500_EGPIO_IRQ_BASE,
|
||||
};
|
||||
|
||||
static struct i2c_board_info mop500_i2c0_devices[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("tc3589x", 0x42),
|
||||
.irq = NOMADIK_GPIO_TO_IRQ(217),
|
||||
.platform_data = &mop500_tc35892_data,
|
||||
},
|
||||
};
|
||||
|
||||
#define U8500_I2C_CONTROLLER(id, _slsu, _tft, _rft, clk, _sm) \
|
||||
static struct nmk_i2c_controller u8500_i2c##id##_data = { \
|
||||
/* \
|
||||
@ -161,159 +167,49 @@ U8500_I2C_CONTROLLER(1, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD);
|
||||
U8500_I2C_CONTROLLER(2, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD);
|
||||
U8500_I2C_CONTROLLER(3, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD);
|
||||
|
||||
static struct amba_device *amba_devs[] __initdata = {
|
||||
&ux500_uart0_device,
|
||||
&ux500_uart1_device,
|
||||
&ux500_uart2_device,
|
||||
&u8500_ssp0_device,
|
||||
};
|
||||
|
||||
static const unsigned int ux500_keymap[] = {
|
||||
KEY(2, 5, KEY_END),
|
||||
KEY(4, 1, KEY_POWER),
|
||||
KEY(3, 5, KEY_VOLUMEDOWN),
|
||||
KEY(1, 3, KEY_3),
|
||||
KEY(5, 2, KEY_RIGHT),
|
||||
KEY(5, 0, KEY_9),
|
||||
|
||||
KEY(0, 5, KEY_MENU),
|
||||
KEY(7, 6, KEY_ENTER),
|
||||
KEY(4, 5, KEY_0),
|
||||
KEY(6, 7, KEY_2),
|
||||
KEY(3, 4, KEY_UP),
|
||||
KEY(3, 3, KEY_DOWN),
|
||||
|
||||
KEY(6, 4, KEY_SEND),
|
||||
KEY(6, 2, KEY_BACK),
|
||||
KEY(4, 2, KEY_VOLUMEUP),
|
||||
KEY(5, 5, KEY_1),
|
||||
KEY(4, 3, KEY_LEFT),
|
||||
KEY(3, 2, KEY_7),
|
||||
};
|
||||
|
||||
static const struct matrix_keymap_data ux500_keymap_data = {
|
||||
.keymap = ux500_keymap,
|
||||
.keymap_size = ARRAY_SIZE(ux500_keymap),
|
||||
};
|
||||
|
||||
/*
|
||||
* Nomadik SKE keypad
|
||||
*/
|
||||
#define ROW_PIN_I0 164
|
||||
#define ROW_PIN_I1 163
|
||||
#define ROW_PIN_I2 162
|
||||
#define ROW_PIN_I3 161
|
||||
#define ROW_PIN_I4 156
|
||||
#define ROW_PIN_I5 155
|
||||
#define ROW_PIN_I6 154
|
||||
#define ROW_PIN_I7 153
|
||||
#define COL_PIN_O0 168
|
||||
#define COL_PIN_O1 167
|
||||
#define COL_PIN_O2 166
|
||||
#define COL_PIN_O3 165
|
||||
#define COL_PIN_O4 160
|
||||
#define COL_PIN_O5 159
|
||||
#define COL_PIN_O6 158
|
||||
#define COL_PIN_O7 157
|
||||
|
||||
#define SKE_KPD_MAX_ROWS 8
|
||||
#define SKE_KPD_MAX_COLS 8
|
||||
|
||||
static int ske_kp_rows[] = {
|
||||
ROW_PIN_I0, ROW_PIN_I1, ROW_PIN_I2, ROW_PIN_I3,
|
||||
ROW_PIN_I4, ROW_PIN_I5, ROW_PIN_I6, ROW_PIN_I7,
|
||||
};
|
||||
|
||||
/*
|
||||
* ske_set_gpio_row: request and set gpio rows
|
||||
*/
|
||||
static int ske_set_gpio_row(int gpio)
|
||||
static void __init mop500_i2c_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gpio_request(gpio, "ske-kp");
|
||||
if (ret < 0) {
|
||||
pr_err("ske_set_gpio_row: gpio request failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_direction_output(gpio, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("ske_set_gpio_row: gpio direction failed\n");
|
||||
gpio_free(gpio);
|
||||
}
|
||||
|
||||
return ret;
|
||||
db8500_add_i2c0(&u8500_i2c0_data);
|
||||
db8500_add_i2c1(&u8500_i2c1_data);
|
||||
db8500_add_i2c2(&u8500_i2c2_data);
|
||||
db8500_add_i2c3(&u8500_i2c3_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* ske_kp_init - enable the gpio configuration
|
||||
*/
|
||||
static int ske_kp_init(void)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < SKE_KPD_MAX_ROWS; i++) {
|
||||
ret = ske_set_gpio_row(ske_kp_rows[i]);
|
||||
if (ret < 0) {
|
||||
pr_err("ske_kp_init: failed init\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ske_keypad_platform_data ske_keypad_board = {
|
||||
.init = ske_kp_init,
|
||||
.keymap_data = &ux500_keymap_data,
|
||||
.no_autorepeat = true,
|
||||
.krow = SKE_KPD_MAX_ROWS, /* 8x8 matrix */
|
||||
.kcol = SKE_KPD_MAX_COLS,
|
||||
.debounce_ms = 40, /* in millsecs */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* add any platform devices here - TODO */
|
||||
static struct platform_device *platform_devs[] __initdata = {
|
||||
&u8500_i2c0_device,
|
||||
&ux500_i2c1_device,
|
||||
&ux500_i2c2_device,
|
||||
&ux500_i2c3_device,
|
||||
&ux500_ske_keypad_device,
|
||||
};
|
||||
|
||||
static void __init mop500_spi_init(void)
|
||||
{
|
||||
db8500_add_ssp0(&ssp0_platform_data);
|
||||
}
|
||||
|
||||
static void __init mop500_uart_init(void)
|
||||
{
|
||||
db8500_add_uart0();
|
||||
db8500_add_uart1();
|
||||
db8500_add_uart2();
|
||||
}
|
||||
|
||||
static void __init u8500_init_machine(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
u8500_init_devices();
|
||||
|
||||
nmk_config_pins(mop500_pins, ARRAY_SIZE(mop500_pins));
|
||||
|
||||
u8500_i2c0_device.dev.platform_data = &u8500_i2c0_data;
|
||||
ux500_i2c1_device.dev.platform_data = &u8500_i2c1_data;
|
||||
ux500_i2c2_device.dev.platform_data = &u8500_i2c2_data;
|
||||
ux500_i2c3_device.dev.platform_data = &u8500_i2c3_data;
|
||||
ux500_ske_keypad_device.dev.platform_data = &ske_keypad_board;
|
||||
|
||||
u8500_ssp0_device.dev.platform_data = &ssp0_platform_data;
|
||||
|
||||
/* Register the active AMBA devices on this board */
|
||||
for (i = 0; i < ARRAY_SIZE(amba_devs); i++)
|
||||
amba_device_register(amba_devs[i], &iomem_resource);
|
||||
|
||||
platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs));
|
||||
|
||||
mop500_i2c_init();
|
||||
mop500_sdi_init();
|
||||
mop500_spi_init();
|
||||
mop500_uart_init();
|
||||
|
||||
/* If HW is early drop (ED) or V1.0 then use SPI to access AB8500 */
|
||||
if (cpu_is_u8500ed() || cpu_is_u8500v10())
|
||||
spi_register_board_info(ab8500_spi_devices,
|
||||
ARRAY_SIZE(ab8500_spi_devices));
|
||||
else /* If HW is v.1.1 or later use I2C to access AB8500 */
|
||||
platform_device_register(&ab8500_device);
|
||||
mop500_keypad_init();
|
||||
|
||||
platform_device_register(&ab8500_device);
|
||||
|
||||
i2c_register_board_info(0, mop500_i2c0_devices,
|
||||
ARRAY_SIZE(mop500_i2c0_devices));
|
||||
}
|
||||
|
||||
MACHINE_START(U8500, "ST-Ericsson MOP500 platform")
|
||||
|
@ -7,6 +7,15 @@
|
||||
#ifndef __BOARD_MOP500_H
|
||||
#define __BOARD_MOP500_H
|
||||
|
||||
#define MOP500_EGPIO(x) (NOMADIK_NR_GPIO + (x))
|
||||
|
||||
/* GPIOs on the TC35892 expander */
|
||||
#define GPIO_SDMMC_CD MOP500_EGPIO(3)
|
||||
#define GPIO_SDMMC_EN MOP500_EGPIO(17)
|
||||
#define GPIO_SDMMC_1V8_3V_SEL MOP500_EGPIO(18)
|
||||
|
||||
extern void mop500_sdi_init(void);
|
||||
extern void mop500_sdi_tc35892_init(void);
|
||||
extern void mop500_keypad_init(void);
|
||||
|
||||
#endif
|
||||
|
49
arch/arm/mach-ux500/board-u5500-sdi.c
Normal file
49
arch/arm/mach-ux500/board-u5500-sdi.c
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Hanumath Prasad <ulf.hansson@stericsson.com>
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#include <linux/amba/mmci.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <plat/pincfg.h>
|
||||
#include <mach/db5500-regs.h>
|
||||
#include <plat/ste_dma40.h>
|
||||
|
||||
#include "pins-db5500.h"
|
||||
#include "devices-db5500.h"
|
||||
#include "ste-dma40-db5500.h"
|
||||
|
||||
static pin_cfg_t u5500_sdi_pins[] = {
|
||||
/* SDI0 (POP eMMC) */
|
||||
GPIO5_MC0_DAT0 | PIN_DIR_INPUT | PIN_PULL_UP,
|
||||
GPIO6_MC0_DAT1 | PIN_DIR_INPUT | PIN_PULL_UP,
|
||||
GPIO7_MC0_DAT2 | PIN_DIR_INPUT | PIN_PULL_UP,
|
||||
GPIO8_MC0_DAT3 | PIN_DIR_INPUT | PIN_PULL_UP,
|
||||
GPIO9_MC0_DAT4 | PIN_DIR_INPUT | PIN_PULL_UP,
|
||||
GPIO10_MC0_DAT5 | PIN_DIR_INPUT | PIN_PULL_UP,
|
||||
GPIO11_MC0_DAT6 | PIN_DIR_INPUT | PIN_PULL_UP,
|
||||
GPIO12_MC0_DAT7 | PIN_DIR_INPUT | PIN_PULL_UP,
|
||||
GPIO13_MC0_CMD | PIN_DIR_INPUT | PIN_PULL_UP,
|
||||
GPIO14_MC0_CLK | PIN_DIR_OUTPUT | PIN_VAL_LOW,
|
||||
};
|
||||
|
||||
static struct mmci_platform_data u5500_sdi0_data = {
|
||||
.ocr_mask = MMC_VDD_165_195,
|
||||
.f_max = 50000000,
|
||||
.capabilities = MMC_CAP_4_BIT_DATA |
|
||||
MMC_CAP_8_BIT_DATA |
|
||||
MMC_CAP_MMC_HIGHSPEED,
|
||||
.gpio_cd = -1,
|
||||
.gpio_wp = -1,
|
||||
};
|
||||
|
||||
void __init u5500_sdi_init(void)
|
||||
{
|
||||
nmk_config_pins(u5500_sdi_pins, ARRAY_SIZE(u5500_sdi_pins));
|
||||
|
||||
db5500_add_sdi0(&u5500_sdi0_data);
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach-types.h>
|
||||
@ -17,20 +18,24 @@
|
||||
#include <mach/devices.h>
|
||||
#include <mach/setup.h>
|
||||
|
||||
static struct amba_device *amba_board_devs[] __initdata = {
|
||||
&ux500_uart0_device,
|
||||
&ux500_uart1_device,
|
||||
&ux500_uart2_device,
|
||||
};
|
||||
#include "devices-db5500.h"
|
||||
|
||||
static void __init u5500_uart_init(void)
|
||||
{
|
||||
db5500_add_uart0();
|
||||
db5500_add_uart1();
|
||||
db5500_add_uart2();
|
||||
}
|
||||
|
||||
static void __init u5500_init_machine(void)
|
||||
{
|
||||
u5500_init_devices();
|
||||
|
||||
amba_add_devices(amba_board_devs, ARRAY_SIZE(amba_board_devs));
|
||||
u5500_sdi_init();
|
||||
u5500_uart_init();
|
||||
}
|
||||
|
||||
MACHINE_START(U8500, "ST-Ericsson U5500 Platform")
|
||||
MACHINE_START(U5500, "ST-Ericsson U5500 Platform")
|
||||
.boot_params = 0x00000100,
|
||||
.map_io = u5500_map_io,
|
||||
.init_irq = ux500_init_irq,
|
||||
|
@ -20,6 +20,12 @@
|
||||
#include <mach/hardware.h>
|
||||
#include "clock.h"
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h> /* for copy_from_user */
|
||||
static LIST_HEAD(clk_list);
|
||||
#endif
|
||||
|
||||
#define PRCC_PCKEN 0x00
|
||||
#define PRCC_PCKDIS 0x04
|
||||
#define PRCC_KCKEN 0x08
|
||||
@ -133,7 +139,7 @@ static unsigned long clk_mtu_get_rate(struct clk *clk)
|
||||
{
|
||||
void __iomem *addr = __io_address(UX500_PRCMU_BASE)
|
||||
+ PRCM_TCR;
|
||||
u32 tcr = readl(addr);
|
||||
u32 tcr;
|
||||
int mtu = (int) clk->data;
|
||||
/*
|
||||
* One of these is selected eventually
|
||||
@ -144,6 +150,14 @@ static unsigned long clk_mtu_get_rate(struct clk *clk)
|
||||
unsigned long mturate;
|
||||
unsigned long retclk;
|
||||
|
||||
/*
|
||||
* On a startup, always conifgure the TCR to the doze mode;
|
||||
* bootloaders do it for us. Do this in the kernel too.
|
||||
*/
|
||||
writel(PRCM_TCR_DOZE_MODE, addr);
|
||||
|
||||
tcr = readl(addr);
|
||||
|
||||
/* Get the rate from the parent as a default */
|
||||
if (clk->parent_periph)
|
||||
mturate = clk_get_rate(clk->parent_periph);
|
||||
@ -153,45 +167,6 @@ static unsigned long clk_mtu_get_rate(struct clk *clk)
|
||||
/* We need to be connected SOMEWHERE */
|
||||
BUG();
|
||||
|
||||
/*
|
||||
* Are we in doze mode?
|
||||
* In this mode the parent peripheral or the fixed 32768 Hz
|
||||
* clock is fed into the block.
|
||||
*/
|
||||
if (!(tcr & PRCM_TCR_DOZE_MODE)) {
|
||||
/*
|
||||
* Here we're using the clock input from the APE ULP
|
||||
* clock domain. But first: are the timers stopped?
|
||||
*/
|
||||
if (tcr & PRCM_TCR_STOPPED) {
|
||||
clk32k = 0;
|
||||
mturate = 0;
|
||||
} else {
|
||||
/* Else default mode: 0 and 2.4 MHz */
|
||||
clk32k = 0;
|
||||
if (cpu_is_u5500())
|
||||
/* DB5500 divides by 8 */
|
||||
mturate /= 8;
|
||||
else if (cpu_is_u8500ed()) {
|
||||
/*
|
||||
* This clocking setting must not be used
|
||||
* in the ED chip, it is simply not
|
||||
* connected anywhere!
|
||||
*/
|
||||
mturate = 0;
|
||||
BUG();
|
||||
} else
|
||||
/*
|
||||
* In this mode the ulp38m4 clock is divided
|
||||
* by a factor 16, on the DB8500 typically
|
||||
* 38400000 / 16 ~ 2.4 MHz.
|
||||
* TODO: Replace the constant with a reference
|
||||
* to the ULP source once this is modeled.
|
||||
*/
|
||||
mturate = 38400000 / 16;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the clock selected for this MTU */
|
||||
if (tcr & (1 << mtu))
|
||||
retclk = clk32k;
|
||||
@ -317,6 +292,7 @@ static struct clkops clk_prcc_ops = {
|
||||
};
|
||||
|
||||
static struct clk clk_32khz = {
|
||||
.name = "clk_32khz",
|
||||
.rate = 32000,
|
||||
};
|
||||
|
||||
@ -366,94 +342,96 @@ static DEFINE_PRCMU_CLK(uiccclk, 0x4, 1, UICCCLK); /* v1 */
|
||||
*/
|
||||
|
||||
/* Peripheral Cluster #1 */
|
||||
static DEFINE_PRCC_CLK(1, i2c4, 10, 9, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(1, i2c4, 10, 9, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(1, gpio0, 9, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(1, slimbus0, 8, 8, &clk_slimclk);
|
||||
static DEFINE_PRCC_CLK(1, spi3_ed, 7, 7, NULL);
|
||||
static DEFINE_PRCC_CLK(1, spi3_v1, 7, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(1, i2c2, 6, 6, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(1, slimbus0, 8, 8, &clk_slimclk);
|
||||
static DEFINE_PRCC_CLK(1, spi3_ed, 7, 7, NULL);
|
||||
static DEFINE_PRCC_CLK(1, spi3_v1, 7, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(1, i2c2, 6, 6, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(1, sdi0, 5, 5, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(1, msp1_ed, 4, 4, &clk_msp02clk);
|
||||
static DEFINE_PRCC_CLK(1, msp1_v1, 4, 4, &clk_msp1clk);
|
||||
static DEFINE_PRCC_CLK(1, msp0, 3, 3, &clk_msp02clk);
|
||||
static DEFINE_PRCC_CLK(1, i2c1, 2, 2, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(1, uart1, 1, 1, &clk_uartclk);
|
||||
static DEFINE_PRCC_CLK(1, uart0, 0, 0, &clk_uartclk);
|
||||
static DEFINE_PRCC_CLK(1, msp1_ed, 4, 4, &clk_msp02clk);
|
||||
static DEFINE_PRCC_CLK(1, msp1_v1, 4, 4, &clk_msp1clk);
|
||||
static DEFINE_PRCC_CLK(1, msp0, 3, 3, &clk_msp02clk);
|
||||
static DEFINE_PRCC_CLK(1, i2c1, 2, 2, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(1, uart1, 1, 1, &clk_uartclk);
|
||||
static DEFINE_PRCC_CLK(1, uart0, 0, 0, &clk_uartclk);
|
||||
|
||||
/* Peripheral Cluster #2 */
|
||||
|
||||
static DEFINE_PRCC_CLK(2, gpio1_ed, 12, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, ssitx_ed, 11, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, ssirx_ed, 10, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, spi0_ed, 9, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, sdi3_ed, 8, 6, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, sdi1_ed, 7, 5, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, msp2_ed, 6, 4, &clk_msp02clk);
|
||||
static DEFINE_PRCC_CLK(2, sdi4_ed, 4, 2, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, ssitx_ed, 11, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, ssirx_ed, 10, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, spi0_ed, 9, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, sdi3_ed, 8, 6, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, sdi1_ed, 7, 5, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, msp2_ed, 6, 4, &clk_msp02clk);
|
||||
static DEFINE_PRCC_CLK(2, sdi4_ed, 4, 2, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, pwl_ed, 3, 1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, spi1_ed, 2, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, spi2_ed, 1, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, i2c3_ed, 0, 0, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(2, spi1_ed, 2, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, spi2_ed, 1, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, i2c3_ed, 0, 0, &clk_i2cclk);
|
||||
|
||||
static DEFINE_PRCC_CLK(2, gpio1_v1, 11, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, ssitx_v1, 10, 7, NULL);
|
||||
static DEFINE_PRCC_CLK(2, ssirx_v1, 9, 6, NULL);
|
||||
static DEFINE_PRCC_CLK(2, spi0_v1, 8, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, sdi3_v1, 7, 5, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, sdi1_v1, 6, 4, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, msp2_v1, 5, 3, &clk_msp02clk);
|
||||
static DEFINE_PRCC_CLK(2, sdi4_v1, 4, 2, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, ssitx_v1, 10, 7, NULL);
|
||||
static DEFINE_PRCC_CLK(2, ssirx_v1, 9, 6, NULL);
|
||||
static DEFINE_PRCC_CLK(2, spi0_v1, 8, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, sdi3_v1, 7, 5, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, sdi1_v1, 6, 4, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, msp2_v1, 5, 3, &clk_msp02clk);
|
||||
static DEFINE_PRCC_CLK(2, sdi4_v1, 4, 2, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(2, pwl_v1, 3, 1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, spi1_v1, 2, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, spi2_v1, 1, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, i2c3_v1, 0, 0, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(2, spi1_v1, 2, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, spi2_v1, 1, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(2, i2c3_v1, 0, 0, &clk_i2cclk);
|
||||
|
||||
/* Peripheral Cluster #3 */
|
||||
static DEFINE_PRCC_CLK(3, gpio2, 8, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(3, sdi5, 7, 7, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(3, uart2, 6, 6, &clk_uartclk);
|
||||
static DEFINE_PRCC_CLK(3, ske, 5, 5, &clk_32khz);
|
||||
static DEFINE_PRCC_CLK(3, sdi2, 4, 4, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(3, i2c0, 3, 3, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(3, ssp1_ed, 2, 2, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(3, ssp0_ed, 1, 1, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(3, ssp1_v1, 2, 2, &clk_sspclk);
|
||||
static DEFINE_PRCC_CLK(3, ssp0_v1, 1, 1, &clk_sspclk);
|
||||
static DEFINE_PRCC_CLK(3, fsmc, 0, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(3, gpio2, 8, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(3, sdi5, 7, 7, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(3, uart2, 6, 6, &clk_uartclk);
|
||||
static DEFINE_PRCC_CLK(3, ske, 5, 5, &clk_32khz);
|
||||
static DEFINE_PRCC_CLK(3, sdi2, 4, 4, &clk_sdmmcclk);
|
||||
static DEFINE_PRCC_CLK(3, i2c0, 3, 3, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(3, ssp1_ed, 2, 2, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(3, ssp0_ed, 1, 1, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(3, ssp1_v1, 2, 2, &clk_sspclk);
|
||||
static DEFINE_PRCC_CLK(3, ssp0_v1, 1, 1, &clk_sspclk);
|
||||
static DEFINE_PRCC_CLK(3, fsmc, 0, -1, NULL);
|
||||
|
||||
/* Peripheral Cluster #4 is in the always on domain */
|
||||
|
||||
/* Peripheral Cluster #5 */
|
||||
static DEFINE_PRCC_CLK(5, gpio3, 1, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(5, usb_ed, 0, 0, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(5, usb_v1, 0, 0, NULL);
|
||||
static DEFINE_PRCC_CLK(5, gpio3, 1, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(5, usb_ed, 0, 0, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(5, usb_v1, 0, 0, NULL);
|
||||
|
||||
/* Peripheral Cluster #6 */
|
||||
|
||||
/* MTU ID in data */
|
||||
static DEFINE_PRCC_CLK_CUSTOM(6, mtu1_v1, 8, -1, NULL, clk_mtu_get_rate, 1);
|
||||
static DEFINE_PRCC_CLK_CUSTOM(6, mtu0_v1, 7, -1, NULL, clk_mtu_get_rate, 0);
|
||||
static DEFINE_PRCC_CLK(6, cfgreg_v1, 6, 6, NULL);
|
||||
static DEFINE_PRCC_CLK(6, dmc_ed, 6, 6, NULL);
|
||||
static DEFINE_PRCC_CLK(6, hash1, 5, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(6, unipro_v1, 4, 1, &clk_uniproclk);
|
||||
static DEFINE_PRCC_CLK(6, cryp1_ed, 4, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(6, pka, 3, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(6, hash0, 2, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(6, cryp0, 1, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(6, rng_ed, 0, 0, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(6, rng_v1, 0, 0, &clk_rngclk);
|
||||
static DEFINE_PRCC_CLK(6, cfgreg_v1, 6, 6, NULL);
|
||||
static DEFINE_PRCC_CLK(6, dmc_ed, 6, 6, NULL);
|
||||
static DEFINE_PRCC_CLK(6, hash1, 5, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(6, unipro_v1, 4, 1, &clk_uniproclk);
|
||||
static DEFINE_PRCC_CLK(6, cryp1_ed, 4, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(6, pka, 3, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(6, hash0, 2, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(6, cryp0, 1, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(6, rng_ed, 0, 0, &clk_i2cclk);
|
||||
static DEFINE_PRCC_CLK(6, rng_v1, 0, 0, &clk_rngclk);
|
||||
|
||||
/* Peripheral Cluster #7 */
|
||||
|
||||
static DEFINE_PRCC_CLK(7, tzpc0_ed, 4, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(7, tzpc0_ed, 4, -1, NULL);
|
||||
/* MTU ID in data */
|
||||
static DEFINE_PRCC_CLK_CUSTOM(7, mtu1_ed, 3, -1, NULL, clk_mtu_get_rate, 1);
|
||||
static DEFINE_PRCC_CLK_CUSTOM(7, mtu0_ed, 2, -1, NULL, clk_mtu_get_rate, 0);
|
||||
static DEFINE_PRCC_CLK(7, wdg_ed, 1, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(7, cfgreg_ed, 0, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(7, wdg_ed, 1, -1, NULL);
|
||||
static DEFINE_PRCC_CLK(7, cfgreg_ed, 0, -1, NULL);
|
||||
|
||||
static struct clk clk_dummy_apb_pclk;
|
||||
static struct clk clk_dummy_apb_pclk = {
|
||||
.name = "apb_pclk",
|
||||
};
|
||||
|
||||
static struct clk_lookup u8500_common_clks[] = {
|
||||
CLK(dummy_apb_pclk, NULL, "apb_pclk"),
|
||||
@ -554,7 +532,7 @@ static struct clk_lookup u8500_ed_clks[] = {
|
||||
|
||||
static struct clk_lookup u8500_v1_clks[] = {
|
||||
/* Peripheral Cluster #1 */
|
||||
CLK(i2c4, "nmk-i2c.4", NULL),
|
||||
CLK(i2c4, "nmk-i2c.4", NULL),
|
||||
CLK(spi3_v1, "spi3", NULL),
|
||||
CLK(msp1_v1, "msp1", NULL),
|
||||
|
||||
@ -599,6 +577,183 @@ static struct clk_lookup u8500_v1_clks[] = {
|
||||
CLK(uiccclk, "uicc", NULL),
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
/*
|
||||
* debugfs support to trace clock tree hierarchy and attributes with
|
||||
* powerdebug
|
||||
*/
|
||||
static struct dentry *clk_debugfs_root;
|
||||
|
||||
void __init clk_debugfs_add_table(struct clk_lookup *cl, size_t num)
|
||||
{
|
||||
while (num--) {
|
||||
/* Check that the clock has not been already registered */
|
||||
if (!(cl->clk->list.prev != cl->clk->list.next))
|
||||
list_add_tail(&cl->clk->list, &clk_list);
|
||||
|
||||
cl++;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t usecount_dbg_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct clk *clk = file->f_dentry->d_inode->i_private;
|
||||
char cusecount[128];
|
||||
unsigned int len;
|
||||
|
||||
len = sprintf(cusecount, "%u\n", clk->enabled);
|
||||
return simple_read_from_buffer(buf, size, off, cusecount, len);
|
||||
}
|
||||
|
||||
static ssize_t rate_dbg_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct clk *clk = file->f_dentry->d_inode->i_private;
|
||||
char crate[128];
|
||||
unsigned int rate;
|
||||
unsigned int len;
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
len = sprintf(crate, "%u\n", rate);
|
||||
return simple_read_from_buffer(buf, size, off, crate, len);
|
||||
}
|
||||
|
||||
static const struct file_operations usecount_fops = {
|
||||
.read = usecount_dbg_read,
|
||||
};
|
||||
|
||||
static const struct file_operations set_rate_fops = {
|
||||
.read = rate_dbg_read,
|
||||
};
|
||||
|
||||
static struct dentry *clk_debugfs_register_dir(struct clk *c,
|
||||
struct dentry *p_dentry)
|
||||
{
|
||||
struct dentry *d, *clk_d, *child, *child_tmp;
|
||||
char s[255];
|
||||
char *p = s;
|
||||
|
||||
if (c->name == NULL)
|
||||
p += sprintf(p, "BUG");
|
||||
else
|
||||
p += sprintf(p, "%s", c->name);
|
||||
|
||||
clk_d = debugfs_create_dir(s, p_dentry);
|
||||
if (!clk_d)
|
||||
return NULL;
|
||||
|
||||
d = debugfs_create_file("usecount", S_IRUGO,
|
||||
clk_d, c, &usecount_fops);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
d = debugfs_create_file("rate", S_IRUGO,
|
||||
clk_d, c, &set_rate_fops);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
/*
|
||||
* TODO : not currently available in ux500
|
||||
* d = debugfs_create_x32("flags", S_IRUGO, clk_d, (u32 *)&c->flags);
|
||||
* if (!d)
|
||||
* goto err_out;
|
||||
*/
|
||||
|
||||
return clk_d;
|
||||
|
||||
err_out:
|
||||
d = clk_d;
|
||||
list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child)
|
||||
debugfs_remove(child);
|
||||
debugfs_remove(clk_d);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void clk_debugfs_remove_dir(struct dentry *cdentry)
|
||||
{
|
||||
struct dentry *d, *child, *child_tmp;
|
||||
|
||||
d = cdentry;
|
||||
list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child)
|
||||
debugfs_remove(child);
|
||||
debugfs_remove(cdentry);
|
||||
return ;
|
||||
}
|
||||
|
||||
static int clk_debugfs_register_one(struct clk *c)
|
||||
{
|
||||
struct clk *pa = c->parent_periph;
|
||||
struct clk *bpa = c->parent_cluster;
|
||||
|
||||
if (!(bpa && !pa)) {
|
||||
c->dent = clk_debugfs_register_dir(c,
|
||||
pa ? pa->dent : clk_debugfs_root);
|
||||
if (!c->dent)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (bpa) {
|
||||
c->dent_bus = clk_debugfs_register_dir(c,
|
||||
bpa->dent_bus ? bpa->dent_bus : bpa->dent);
|
||||
if ((!c->dent_bus) && (c->dent)) {
|
||||
clk_debugfs_remove_dir(c->dent);
|
||||
c->dent = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_debugfs_register(struct clk *c)
|
||||
{
|
||||
int err;
|
||||
struct clk *pa = c->parent_periph;
|
||||
struct clk *bpa = c->parent_cluster;
|
||||
|
||||
if (pa && (!pa->dent && !pa->dent_bus)) {
|
||||
err = clk_debugfs_register(pa);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (bpa && (!bpa->dent && !bpa->dent_bus)) {
|
||||
err = clk_debugfs_register(bpa);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((!c->dent) && (!c->dent_bus)) {
|
||||
err = clk_debugfs_register_one(c);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init clk_debugfs_init(void)
|
||||
{
|
||||
struct clk *c;
|
||||
struct dentry *d;
|
||||
int err;
|
||||
|
||||
d = debugfs_create_dir("clock", NULL);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
clk_debugfs_root = d;
|
||||
|
||||
list_for_each_entry(c, &clk_list, list) {
|
||||
err = clk_debugfs_register(c);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
return 0;
|
||||
err_out:
|
||||
debugfs_remove_recursive(clk_debugfs_root);
|
||||
return err;
|
||||
}
|
||||
|
||||
late_initcall(clk_debugfs_init);
|
||||
#endif /* defined(CONFIG_DEBUG_FS) */
|
||||
|
||||
int __init clk_init(void)
|
||||
{
|
||||
if (cpu_is_u8500ed()) {
|
||||
@ -609,7 +764,8 @@ int __init clk_init(void)
|
||||
/* Clock tree for U5500 not implemented yet */
|
||||
clk_prcc_ops.enable = clk_prcc_ops.disable = NULL;
|
||||
clk_prcmu_ops.enable = clk_prcmu_ops.disable = NULL;
|
||||
clk_per6clk.rate = 26000000;
|
||||
clk_uartclk.rate = 36360000;
|
||||
clk_sdmmcclk.rate = 99900000;
|
||||
}
|
||||
|
||||
clkdev_add_table(u8500_common_clks, ARRAY_SIZE(u8500_common_clks));
|
||||
@ -618,5 +774,12 @@ int __init clk_init(void)
|
||||
else
|
||||
clkdev_add_table(u8500_v1_clks, ARRAY_SIZE(u8500_v1_clks));
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
clk_debugfs_add_table(u8500_common_clks, ARRAY_SIZE(u8500_common_clks));
|
||||
if (cpu_is_u8500ed())
|
||||
clk_debugfs_add_table(u8500_ed_clks, ARRAY_SIZE(u8500_ed_clks));
|
||||
else
|
||||
clk_debugfs_add_table(u8500_v1_clks, ARRAY_SIZE(u8500_v1_clks));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -90,6 +90,10 @@ struct clk {
|
||||
|
||||
struct clk *parent_cluster;
|
||||
struct clk *parent_periph;
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
struct dentry *dent; /* For visible tree hierarchy */
|
||||
struct dentry *dent_bus; /* For visible tree hierarchy */
|
||||
#endif
|
||||
};
|
||||
|
||||
#define DEFINE_PRCMU_CLK(_name, _cg_off, _cg_bit, _reg) \
|
||||
|
@ -8,14 +8,19 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
#include <plat/gpio.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/devices.h>
|
||||
#include <mach/setup.h>
|
||||
#include <mach/irqs.h>
|
||||
|
||||
#include "devices-db5500.h"
|
||||
|
||||
static struct map_desc u5500_io_desc[] __initdata = {
|
||||
__IO_DEV_DESC(U5500_GPIO0_BASE, SZ_4K),
|
||||
__IO_DEV_DESC(U5500_GPIO1_BASE, SZ_4K),
|
||||
@ -110,19 +115,32 @@ static struct platform_device mbox2_device = {
|
||||
};
|
||||
|
||||
static struct platform_device *u5500_platform_devs[] __initdata = {
|
||||
&u5500_gpio_devs[0],
|
||||
&u5500_gpio_devs[1],
|
||||
&u5500_gpio_devs[2],
|
||||
&u5500_gpio_devs[3],
|
||||
&u5500_gpio_devs[4],
|
||||
&u5500_gpio_devs[5],
|
||||
&u5500_gpio_devs[6],
|
||||
&u5500_gpio_devs[7],
|
||||
&mbox0_device,
|
||||
&mbox1_device,
|
||||
&mbox2_device,
|
||||
};
|
||||
|
||||
static resource_size_t __initdata db5500_gpio_base[] = {
|
||||
U5500_GPIOBANK0_BASE,
|
||||
U5500_GPIOBANK1_BASE,
|
||||
U5500_GPIOBANK2_BASE,
|
||||
U5500_GPIOBANK3_BASE,
|
||||
U5500_GPIOBANK4_BASE,
|
||||
U5500_GPIOBANK5_BASE,
|
||||
U5500_GPIOBANK6_BASE,
|
||||
U5500_GPIOBANK7_BASE,
|
||||
};
|
||||
|
||||
static void __init db5500_add_gpios(void)
|
||||
{
|
||||
struct nmk_gpio_platform_data pdata = {
|
||||
/* No custom data yet */
|
||||
};
|
||||
|
||||
dbx500_add_gpios(ARRAY_AND_SIZE(db5500_gpio_base),
|
||||
IRQ_DB5500_GPIO0, &pdata);
|
||||
}
|
||||
|
||||
void __init u5500_map_io(void)
|
||||
{
|
||||
ux500_map_io();
|
||||
@ -132,7 +150,9 @@ void __init u5500_map_io(void)
|
||||
|
||||
void __init u5500_init_devices(void)
|
||||
{
|
||||
ux500_init_devices();
|
||||
db5500_add_gpios();
|
||||
db5500_dma_init();
|
||||
db5500_add_rtc();
|
||||
|
||||
platform_add_devices(u5500_platform_devs,
|
||||
ARRAY_SIZE(u5500_platform_devs));
|
||||
|
@ -22,23 +22,15 @@
|
||||
#include <mach/setup.h>
|
||||
#include <mach/devices.h>
|
||||
|
||||
#include "devices-db8500.h"
|
||||
|
||||
static struct platform_device *platform_devs[] __initdata = {
|
||||
&u8500_gpio_devs[0],
|
||||
&u8500_gpio_devs[1],
|
||||
&u8500_gpio_devs[2],
|
||||
&u8500_gpio_devs[3],
|
||||
&u8500_gpio_devs[4],
|
||||
&u8500_gpio_devs[5],
|
||||
&u8500_gpio_devs[6],
|
||||
&u8500_gpio_devs[7],
|
||||
&u8500_gpio_devs[8],
|
||||
&u8500_dma40_device,
|
||||
};
|
||||
|
||||
/* minimum static i/o mapping required to boot U8500 platforms */
|
||||
static struct map_desc u8500_io_desc[] __initdata = {
|
||||
__IO_DEV_DESC(U8500_PRCMU_BASE, SZ_4K),
|
||||
__IO_DEV_DESC(U8500_PRCMU_TCDM_BASE, SZ_4K),
|
||||
__IO_DEV_DESC(U8500_GPIO0_BASE, SZ_4K),
|
||||
__IO_DEV_DESC(U8500_GPIO1_BASE, SZ_4K),
|
||||
__IO_DEV_DESC(U8500_GPIO2_BASE, SZ_4K),
|
||||
@ -46,13 +38,18 @@ static struct map_desc u8500_io_desc[] __initdata = {
|
||||
__MEM_DEV_DESC(U8500_BOOT_ROM_BASE, SZ_1M),
|
||||
};
|
||||
|
||||
static struct map_desc u8500ed_io_desc[] __initdata = {
|
||||
static struct map_desc u8500_ed_io_desc[] __initdata = {
|
||||
__IO_DEV_DESC(U8500_MTU0_BASE_ED, SZ_4K),
|
||||
__IO_DEV_DESC(U8500_CLKRST7_BASE_ED, SZ_8K),
|
||||
};
|
||||
|
||||
static struct map_desc u8500v1_io_desc[] __initdata = {
|
||||
static struct map_desc u8500_v1_io_desc[] __initdata = {
|
||||
__IO_DEV_DESC(U8500_MTU0_BASE, SZ_4K),
|
||||
__IO_DEV_DESC(U8500_PRCMU_TCDM_BASE_V1, SZ_4K),
|
||||
};
|
||||
|
||||
static struct map_desc u8500_v2_io_desc[] __initdata = {
|
||||
__IO_DEV_DESC(U8500_PRCMU_TCDM_BASE, SZ_4K),
|
||||
};
|
||||
|
||||
/*
|
||||
@ -125,14 +122,38 @@ void __init u8500_map_io(void)
|
||||
iotable_init(u8500_io_desc, ARRAY_SIZE(u8500_io_desc));
|
||||
|
||||
if (cpu_is_u8500ed())
|
||||
iotable_init(u8500ed_io_desc, ARRAY_SIZE(u8500ed_io_desc));
|
||||
else
|
||||
iotable_init(u8500v1_io_desc, ARRAY_SIZE(u8500v1_io_desc));
|
||||
iotable_init(u8500_ed_io_desc, ARRAY_SIZE(u8500_ed_io_desc));
|
||||
else if (cpu_is_u8500v1())
|
||||
iotable_init(u8500_v1_io_desc, ARRAY_SIZE(u8500_v1_io_desc));
|
||||
else if (cpu_is_u8500v2())
|
||||
iotable_init(u8500_v2_io_desc, ARRAY_SIZE(u8500_v2_io_desc));
|
||||
|
||||
/* Read out the ASIC ID as early as we can */
|
||||
get_db8500_asic_id();
|
||||
}
|
||||
|
||||
static resource_size_t __initdata db8500_gpio_base[] = {
|
||||
U8500_GPIOBANK0_BASE,
|
||||
U8500_GPIOBANK1_BASE,
|
||||
U8500_GPIOBANK2_BASE,
|
||||
U8500_GPIOBANK3_BASE,
|
||||
U8500_GPIOBANK4_BASE,
|
||||
U8500_GPIOBANK5_BASE,
|
||||
U8500_GPIOBANK6_BASE,
|
||||
U8500_GPIOBANK7_BASE,
|
||||
U8500_GPIOBANK8_BASE,
|
||||
};
|
||||
|
||||
static void __init db8500_add_gpios(void)
|
||||
{
|
||||
struct nmk_gpio_platform_data pdata = {
|
||||
/* No custom data yet */
|
||||
};
|
||||
|
||||
dbx500_add_gpios(ARRAY_AND_SIZE(db8500_gpio_base),
|
||||
IRQ_DB8500_GPIO0, &pdata);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called from the board init
|
||||
*/
|
||||
@ -152,12 +173,13 @@ void __init u8500_init_devices(void)
|
||||
else
|
||||
pr_warning("ASIC: UNKNOWN SILICON VERSION!\n");
|
||||
|
||||
ux500_init_devices();
|
||||
|
||||
if (cpu_is_u8500ed())
|
||||
dma40_u8500ed_fixup();
|
||||
|
||||
/* Register the platform devices */
|
||||
db8500_add_rtc();
|
||||
db8500_add_gpios();
|
||||
|
||||
platform_device_register_simple("cpufreq-u8500", -1, NULL, 0);
|
||||
platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs));
|
||||
|
||||
return ;
|
||||
|
@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
@ -20,6 +19,7 @@
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/setup.h>
|
||||
#include <mach/devices.h>
|
||||
#include <mach/prcmu.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
@ -45,20 +45,11 @@ static struct map_desc ux500_io_desc[] __initdata = {
|
||||
__IO_DEV_DESC(UX500_BACKUPRAM0_BASE, SZ_8K),
|
||||
};
|
||||
|
||||
static struct amba_device *ux500_amba_devs[] __initdata = {
|
||||
&ux500_pl031_device,
|
||||
};
|
||||
|
||||
void __init ux500_map_io(void)
|
||||
{
|
||||
iotable_init(ux500_io_desc, ARRAY_SIZE(ux500_io_desc));
|
||||
}
|
||||
|
||||
void __init ux500_init_devices(void)
|
||||
{
|
||||
amba_add_devices(ux500_amba_devs, ARRAY_SIZE(ux500_amba_devs));
|
||||
}
|
||||
|
||||
void __init ux500_init_irq(void)
|
||||
{
|
||||
gic_dist_init(0, __io_address(UX500_GIC_DIST_BASE), 29);
|
||||
@ -68,6 +59,8 @@ void __init ux500_init_irq(void)
|
||||
* Init clocks here so that they are available for system timer
|
||||
* initialization.
|
||||
*/
|
||||
if (cpu_is_u8500())
|
||||
prcmu_early_init();
|
||||
clk_init();
|
||||
}
|
||||
|
||||
|
211
arch/arm/mach-ux500/cpufreq.c
Normal file
211
arch/arm/mach-ux500/cpufreq.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* CPU frequency scaling for u8500
|
||||
* Inspired by linux/arch/arm/mach-davinci/cpufreq.c
|
||||
*
|
||||
* Copyright (C) STMicroelectronics 2009
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
*
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com>
|
||||
* Author: Martin Persson <martin.persson@stericsson.com>
|
||||
* Author: Jonas Aaberg <jonas.aberg@stericsson.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/prcmu.h>
|
||||
#include <mach/prcmu-defs.h>
|
||||
|
||||
#define DRIVER_NAME "cpufreq-u8500"
|
||||
#define CPUFREQ_NAME "u8500"
|
||||
|
||||
static struct device *dev;
|
||||
|
||||
static struct cpufreq_frequency_table freq_table[] = {
|
||||
[0] = {
|
||||
.index = 0,
|
||||
.frequency = 200000,
|
||||
},
|
||||
[1] = {
|
||||
.index = 1,
|
||||
.frequency = 300000,
|
||||
},
|
||||
[2] = {
|
||||
.index = 2,
|
||||
.frequency = 600000,
|
||||
},
|
||||
[3] = {
|
||||
/* Used for CPU_OPP_MAX, if available */
|
||||
.index = 3,
|
||||
.frequency = CPUFREQ_TABLE_END,
|
||||
},
|
||||
[4] = {
|
||||
.index = 4,
|
||||
.frequency = CPUFREQ_TABLE_END,
|
||||
},
|
||||
};
|
||||
|
||||
static enum prcmu_cpu_opp index2opp[] = {
|
||||
CPU_OPP_EXT_CLK,
|
||||
CPU_OPP_50,
|
||||
CPU_OPP_100,
|
||||
CPU_OPP_MAX
|
||||
};
|
||||
|
||||
static int u8500_cpufreq_verify_speed(struct cpufreq_policy *policy)
|
||||
{
|
||||
return cpufreq_frequency_table_verify(policy, freq_table);
|
||||
}
|
||||
|
||||
static int u8500_cpufreq_target(struct cpufreq_policy *policy,
|
||||
unsigned int target_freq,
|
||||
unsigned int relation)
|
||||
{
|
||||
struct cpufreq_freqs freqs;
|
||||
unsigned int index;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Ensure desired rate is within allowed range. Some govenors
|
||||
* (ondemand) will just pass target_freq=0 to get the minimum.
|
||||
*/
|
||||
if (target_freq < policy->cpuinfo.min_freq)
|
||||
target_freq = policy->cpuinfo.min_freq;
|
||||
if (target_freq > policy->cpuinfo.max_freq)
|
||||
target_freq = policy->cpuinfo.max_freq;
|
||||
|
||||
ret = cpufreq_frequency_table_target(policy, freq_table,
|
||||
target_freq, relation, &index);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Could not look up next frequency\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
freqs.old = policy->cur;
|
||||
freqs.new = freq_table[index].frequency;
|
||||
freqs.cpu = policy->cpu;
|
||||
|
||||
if (freqs.old == freqs.new) {
|
||||
dev_dbg(dev, "Current and target frequencies are equal\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "transition: %u --> %u\n", freqs.old, freqs.new);
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||
|
||||
ret = prcmu_set_cpu_opp(index2opp[index]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set OPP level\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int u8500_cpufreq_getspeed(unsigned int cpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; prcmu_get_cpu_opp() != index2opp[i]; i++)
|
||||
;
|
||||
return freq_table[i].frequency;
|
||||
}
|
||||
|
||||
static int __cpuinit u8500_cpu_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
int res;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(index2opp) + 1 != ARRAY_SIZE(freq_table));
|
||||
|
||||
if (cpu_is_u8500v2()) {
|
||||
freq_table[1].frequency = 400000;
|
||||
freq_table[2].frequency = 800000;
|
||||
if (prcmu_has_arm_maxopp())
|
||||
freq_table[3].frequency = 1000000;
|
||||
}
|
||||
|
||||
/* get policy fields based on the table */
|
||||
res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
|
||||
if (!res)
|
||||
cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
|
||||
else {
|
||||
dev_err(dev, "u8500-cpufreq : Failed to read policy table\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
policy->min = policy->cpuinfo.min_freq;
|
||||
policy->max = policy->cpuinfo.max_freq;
|
||||
policy->cur = u8500_cpufreq_getspeed(policy->cpu);
|
||||
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
|
||||
|
||||
/*
|
||||
* FIXME : Need to take time measurement across the target()
|
||||
* function with no/some/all drivers in the notification
|
||||
* list.
|
||||
*/
|
||||
policy->cpuinfo.transition_latency = 200 * 1000; /* in ns */
|
||||
|
||||
/* policy sharing between dual CPUs */
|
||||
cpumask_copy(policy->cpus, &cpu_present_map);
|
||||
|
||||
policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct freq_attr *u8500_cpufreq_attr[] = {
|
||||
&cpufreq_freq_attr_scaling_available_freqs,
|
||||
NULL,
|
||||
};
|
||||
static int u8500_cpu_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
cpufreq_frequency_table_put_attr(policy->cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cpufreq_driver u8500_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.flags = CPUFREQ_STICKY,
|
||||
.verify = u8500_cpufreq_verify_speed,
|
||||
.target = u8500_cpufreq_target,
|
||||
.get = u8500_cpufreq_getspeed,
|
||||
.init = u8500_cpu_init,
|
||||
.exit = u8500_cpu_exit,
|
||||
.name = CPUFREQ_NAME,
|
||||
.attr = u8500_cpufreq_attr,
|
||||
};
|
||||
|
||||
static int __init u8500_cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev = &pdev->dev;
|
||||
return cpufreq_register_driver(&u8500_driver);
|
||||
}
|
||||
|
||||
static int __exit u8500_cpufreq_remove(struct platform_device *pdev)
|
||||
{
|
||||
return cpufreq_unregister_driver(&u8500_driver);
|
||||
}
|
||||
|
||||
static struct platform_driver u8500_cpufreq_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.remove = __exit_p(u8500_cpufreq_remove),
|
||||
};
|
||||
|
||||
static int __init u8500_cpufreq_init(void)
|
||||
{
|
||||
return platform_driver_probe(&u8500_cpufreq_driver,
|
||||
&u8500_cpufreq_probe);
|
||||
}
|
||||
|
||||
device_initcall(u8500_cpufreq_init);
|
145
arch/arm/mach-ux500/devices-common.c
Normal file
145
arch/arm/mach-ux500/devices-common.c
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL), version 2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
#include <plat/gpio.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#include "devices-common.h"
|
||||
|
||||
struct amba_device *
|
||||
dbx500_add_amba_device(const char *name, resource_size_t base,
|
||||
int irq, void *pdata, unsigned int periphid)
|
||||
{
|
||||
struct amba_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = kzalloc(sizeof *dev, GFP_KERNEL);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev->dev.init_name = name;
|
||||
|
||||
dev->res.start = base;
|
||||
dev->res.end = base + SZ_4K - 1;
|
||||
dev->res.flags = IORESOURCE_MEM;
|
||||
|
||||
dev->dma_mask = DMA_BIT_MASK(32);
|
||||
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
dev->irq[0] = irq;
|
||||
dev->irq[1] = NO_IRQ;
|
||||
|
||||
dev->periphid = periphid;
|
||||
|
||||
dev->dev.platform_data = pdata;
|
||||
|
||||
ret = amba_device_register(dev, &iomem_resource);
|
||||
if (ret) {
|
||||
kfree(dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static struct platform_device *
|
||||
dbx500_add_platform_device(const char *name, int id, void *pdata,
|
||||
struct resource *res, int resnum)
|
||||
{
|
||||
struct platform_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = platform_device_alloc(name, id);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
|
||||
|
||||
ret = platform_device_add_resources(dev, res, resnum);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
dev->dev.platform_data = pdata;
|
||||
|
||||
ret = platform_device_add(dev);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
return dev;
|
||||
|
||||
out_free:
|
||||
platform_device_put(dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
struct platform_device *
|
||||
dbx500_add_platform_device_4k1irq(const char *name, int id,
|
||||
resource_size_t base,
|
||||
int irq, void *pdata)
|
||||
{
|
||||
struct resource resources[] = {
|
||||
[0] = {
|
||||
.start = base,
|
||||
.end = base + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = irq,
|
||||
.end = irq,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
return dbx500_add_platform_device(name, id, pdata, resources,
|
||||
ARRAY_SIZE(resources));
|
||||
}
|
||||
|
||||
static struct platform_device *
|
||||
dbx500_add_gpio(int id, resource_size_t addr, int irq,
|
||||
struct nmk_gpio_platform_data *pdata)
|
||||
{
|
||||
struct resource resources[] = {
|
||||
{
|
||||
.start = addr,
|
||||
.end = addr + 127,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.start = irq,
|
||||
.end = irq,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
return platform_device_register_resndata(NULL, "gpio", id,
|
||||
resources, ARRAY_SIZE(resources),
|
||||
pdata, sizeof(*pdata));
|
||||
}
|
||||
|
||||
void dbx500_add_gpios(resource_size_t *base, int num, int irq,
|
||||
struct nmk_gpio_platform_data *pdata)
|
||||
{
|
||||
int first = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++, first += 32, irq++) {
|
||||
pdata->first_gpio = first;
|
||||
pdata->first_irq = NOMADIK_GPIO_TO_IRQ(first);
|
||||
|
||||
dbx500_add_gpio(i, base[i], irq, pdata);
|
||||
}
|
||||
}
|
82
arch/arm/mach-ux500/devices-common.h
Normal file
82
arch/arm/mach-ux500/devices-common.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL), version 2.
|
||||
*/
|
||||
|
||||
#ifndef __DEVICES_COMMON_H
|
||||
#define __DEVICES_COMMON_H
|
||||
|
||||
extern struct amba_device *
|
||||
dbx500_add_amba_device(const char *name, resource_size_t base,
|
||||
int irq, void *pdata, unsigned int periphid);
|
||||
|
||||
extern struct platform_device *
|
||||
dbx500_add_platform_device_4k1irq(const char *name, int id,
|
||||
resource_size_t base,
|
||||
int irq, void *pdata);
|
||||
|
||||
struct spi_master_cntlr;
|
||||
|
||||
static inline struct amba_device *
|
||||
dbx500_add_msp_spi(const char *name, resource_size_t base, int irq,
|
||||
struct spi_master_cntlr *pdata)
|
||||
{
|
||||
return dbx500_add_amba_device(name, base, irq, pdata, 0);
|
||||
}
|
||||
|
||||
static inline struct amba_device *
|
||||
dbx500_add_spi(const char *name, resource_size_t base, int irq,
|
||||
struct spi_master_cntlr *pdata)
|
||||
{
|
||||
return dbx500_add_amba_device(name, base, irq, pdata, 0);
|
||||
}
|
||||
|
||||
struct mmci_platform_data;
|
||||
|
||||
static inline struct amba_device *
|
||||
dbx500_add_sdi(const char *name, resource_size_t base, int irq,
|
||||
struct mmci_platform_data *pdata)
|
||||
{
|
||||
return dbx500_add_amba_device(name, base, irq, pdata, 0);
|
||||
}
|
||||
|
||||
static inline struct amba_device *
|
||||
dbx500_add_uart(const char *name, resource_size_t base, int irq)
|
||||
{
|
||||
return dbx500_add_amba_device(name, base, irq, NULL, 0);
|
||||
}
|
||||
|
||||
struct nmk_i2c_controller;
|
||||
|
||||
static inline struct platform_device *
|
||||
dbx500_add_i2c(int id, resource_size_t base, int irq,
|
||||
struct nmk_i2c_controller *pdata)
|
||||
{
|
||||
return dbx500_add_platform_device_4k1irq("nmk-i2c", id, base, irq,
|
||||
pdata);
|
||||
}
|
||||
|
||||
struct msp_i2s_platform_data;
|
||||
|
||||
static inline struct platform_device *
|
||||
dbx500_add_msp_i2s(int id, resource_size_t base, int irq,
|
||||
struct msp_i2s_platform_data *pdata)
|
||||
{
|
||||
return dbx500_add_platform_device_4k1irq("MSP_I2S", id, base, irq,
|
||||
pdata);
|
||||
}
|
||||
|
||||
static inline struct amba_device *
|
||||
dbx500_add_rtc(resource_size_t base, int irq)
|
||||
{
|
||||
return dbx500_add_amba_device("rtc-pl031", base, irq, NULL, 0);
|
||||
}
|
||||
|
||||
struct nmk_gpio_platform_data;
|
||||
|
||||
void dbx500_add_gpios(resource_size_t *base, int num, int irq,
|
||||
struct nmk_gpio_platform_data *pdata);
|
||||
|
||||
#endif
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/devices.h>
|
||||
|
||||
static struct nmk_gpio_platform_data u5500_gpio_data[] = {
|
||||
GPIO_DATA("GPIO-0-31", 0),
|
||||
GPIO_DATA("GPIO-32-63", 32), /* 36..63 not routed to pin */
|
||||
GPIO_DATA("GPIO-64-95", 64), /* 83..95 not routed to pin */
|
||||
GPIO_DATA("GPIO-96-127", 96), /* 102..127 not routed to pin */
|
||||
GPIO_DATA("GPIO-128-159", 128), /* 149..159 not routed to pin */
|
||||
GPIO_DATA("GPIO-160-191", 160),
|
||||
GPIO_DATA("GPIO-192-223", 192),
|
||||
GPIO_DATA("GPIO-224-255", 224), /* 228..255 not routed to pin */
|
||||
};
|
||||
|
||||
static struct resource u5500_gpio_resources[] = {
|
||||
GPIO_RESOURCE(0),
|
||||
GPIO_RESOURCE(1),
|
||||
GPIO_RESOURCE(2),
|
||||
GPIO_RESOURCE(3),
|
||||
GPIO_RESOURCE(4),
|
||||
GPIO_RESOURCE(5),
|
||||
GPIO_RESOURCE(6),
|
||||
GPIO_RESOURCE(7),
|
||||
};
|
||||
|
||||
struct platform_device u5500_gpio_devs[] = {
|
||||
GPIO_DEVICE(0),
|
||||
GPIO_DEVICE(1),
|
||||
GPIO_DEVICE(2),
|
||||
GPIO_DEVICE(3),
|
||||
GPIO_DEVICE(4),
|
||||
GPIO_DEVICE(5),
|
||||
GPIO_DEVICE(6),
|
||||
GPIO_DEVICE(7),
|
||||
};
|
66
arch/arm/mach-ux500/devices-db5500.h
Normal file
66
arch/arm/mach-ux500/devices-db5500.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL), version 2.
|
||||
*/
|
||||
|
||||
#ifndef __DEVICES_DB5500_H
|
||||
#define __DEVICES_DB5500_H
|
||||
|
||||
#include "devices-common.h"
|
||||
|
||||
#define db5500_add_i2c1(pdata) \
|
||||
dbx500_add_i2c(1, U5500_I2C1_BASE, IRQ_DB5500_I2C1, pdata)
|
||||
#define db5500_add_i2c2(pdata) \
|
||||
dbx500_add_i2c(2, U5500_I2C2_BASE, IRQ_DB5500_I2C2, pdata)
|
||||
#define db5500_add_i2c3(pdata) \
|
||||
dbx500_add_i2c(3, U5500_I2C3_BASE, IRQ_DB5500_I2C3, pdata)
|
||||
|
||||
#define db5500_add_msp0_i2s(pdata) \
|
||||
dbx500_add_msp_i2s(0, U5500_MSP0_BASE, IRQ_DB5500_MSP0, pdata)
|
||||
#define db5500_add_msp1_i2s(pdata) \
|
||||
dbx500_add_msp_i2s(1, U5500_MSP1_BASE, IRQ_DB5500_MSP1, pdata)
|
||||
#define db5500_add_msp2_i2s(pdata) \
|
||||
dbx500_add_msp_i2s(2, U5500_MSP2_BASE, IRQ_DB5500_MSP2, pdata)
|
||||
|
||||
#define db5500_add_msp0_spi(pdata) \
|
||||
dbx500_add_msp_spi("msp0", U5500_MSP0_BASE, IRQ_DB5500_MSP0, pdata)
|
||||
#define db5500_add_msp1_spi(pdata) \
|
||||
dbx500_add_msp_spi("msp1", U5500_MSP1_BASE, IRQ_DB5500_MSP1, pdata)
|
||||
#define db5500_add_msp2_spi(pdata) \
|
||||
dbx500_add_msp_spi("msp2", U5500_MSP2_BASE, IRQ_DB5500_MSP2, pdata)
|
||||
|
||||
#define db5500_add_rtc() \
|
||||
dbx500_add_rtc(U5500_RTC_BASE, IRQ_DB5500_RTC);
|
||||
|
||||
#define db5500_add_sdi0(pdata) \
|
||||
dbx500_add_sdi("sdi0", U5500_SDI0_BASE, IRQ_DB5500_SDMMC0, pdata)
|
||||
#define db5500_add_sdi1(pdata) \
|
||||
dbx500_add_sdi("sdi1", U5500_SDI1_BASE, IRQ_DB5500_SDMMC1, pdata)
|
||||
#define db5500_add_sdi2(pdata) \
|
||||
dbx500_add_sdi("sdi2", U5500_SDI2_BASE, IRQ_DB5500_SDMMC2, pdata)
|
||||
#define db5500_add_sdi3(pdata) \
|
||||
dbx500_add_sdi("sdi3", U5500_SDI3_BASE, IRQ_DB5500_SDMMC3, pdata)
|
||||
#define db5500_add_sdi4(pdata) \
|
||||
dbx500_add_sdi("sdi4", U5500_SDI4_BASE, IRQ_DB5500_SDMMC4, pdata)
|
||||
|
||||
#define db5500_add_spi0(pdata) \
|
||||
dbx500_add_spi("spi0", U5500_SPI0_BASE, IRQ_DB5500_SPI0, pdata)
|
||||
#define db5500_add_spi1(pdata) \
|
||||
dbx500_add_spi("spi1", U5500_SPI1_BASE, IRQ_DB5500_SPI1, pdata)
|
||||
#define db5500_add_spi2(pdata) \
|
||||
dbx500_add_spi("spi2", U5500_SPI2_BASE, IRQ_DB5500_SPI2, pdata)
|
||||
#define db5500_add_spi3(pdata) \
|
||||
dbx500_add_spi("spi3", U5500_SPI3_BASE, IRQ_DB5500_SPI3, pdata)
|
||||
|
||||
#define db5500_add_uart0() \
|
||||
dbx500_add_uart("uart0", U5500_UART0_BASE, IRQ_DB5500_UART0)
|
||||
#define db5500_add_uart1() \
|
||||
dbx500_add_uart("uart1", U5500_UART1_BASE, IRQ_DB5500_UART1)
|
||||
#define db5500_add_uart2() \
|
||||
dbx500_add_uart("uart2", U5500_UART2_BASE, IRQ_DB5500_UART2)
|
||||
#define db5500_add_uart3() \
|
||||
dbx500_add_uart("uart3", U5500_UART3_BASE, IRQ_DB5500_UART3)
|
||||
|
||||
#endif
|
@ -19,173 +19,6 @@
|
||||
|
||||
#include "ste-dma40-db8500.h"
|
||||
|
||||
static struct nmk_gpio_platform_data u8500_gpio_data[] = {
|
||||
GPIO_DATA("GPIO-0-31", 0),
|
||||
GPIO_DATA("GPIO-32-63", 32), /* 37..63 not routed to pin */
|
||||
GPIO_DATA("GPIO-64-95", 64),
|
||||
GPIO_DATA("GPIO-96-127", 96), /* 98..127 not routed to pin */
|
||||
GPIO_DATA("GPIO-128-159", 128),
|
||||
GPIO_DATA("GPIO-160-191", 160), /* 172..191 not routed to pin */
|
||||
GPIO_DATA("GPIO-192-223", 192),
|
||||
GPIO_DATA("GPIO-224-255", 224), /* 231..255 not routed to pin */
|
||||
GPIO_DATA("GPIO-256-288", 256), /* 268..288 not routed to pin */
|
||||
};
|
||||
|
||||
static struct resource u8500_gpio_resources[] = {
|
||||
GPIO_RESOURCE(0),
|
||||
GPIO_RESOURCE(1),
|
||||
GPIO_RESOURCE(2),
|
||||
GPIO_RESOURCE(3),
|
||||
GPIO_RESOURCE(4),
|
||||
GPIO_RESOURCE(5),
|
||||
GPIO_RESOURCE(6),
|
||||
GPIO_RESOURCE(7),
|
||||
GPIO_RESOURCE(8),
|
||||
};
|
||||
|
||||
struct platform_device u8500_gpio_devs[] = {
|
||||
GPIO_DEVICE(0),
|
||||
GPIO_DEVICE(1),
|
||||
GPIO_DEVICE(2),
|
||||
GPIO_DEVICE(3),
|
||||
GPIO_DEVICE(4),
|
||||
GPIO_DEVICE(5),
|
||||
GPIO_DEVICE(6),
|
||||
GPIO_DEVICE(7),
|
||||
GPIO_DEVICE(8),
|
||||
};
|
||||
|
||||
struct amba_device u8500_ssp0_device = {
|
||||
.dev = {
|
||||
.coherent_dma_mask = ~0,
|
||||
.init_name = "ssp0",
|
||||
},
|
||||
.res = {
|
||||
.start = U8500_SSP0_BASE,
|
||||
.end = U8500_SSP0_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
.irq = {IRQ_DB8500_SSP0, NO_IRQ },
|
||||
/* ST-Ericsson modified id */
|
||||
.periphid = SSP_PER_ID,
|
||||
};
|
||||
|
||||
static struct resource u8500_i2c0_resources[] = {
|
||||
[0] = {
|
||||
.start = U8500_I2C0_BASE,
|
||||
.end = U8500_I2C0_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = IRQ_DB8500_I2C0,
|
||||
.end = IRQ_DB8500_I2C0,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
struct platform_device u8500_i2c0_device = {
|
||||
.name = "nmk-i2c",
|
||||
.id = 0,
|
||||
.resource = u8500_i2c0_resources,
|
||||
.num_resources = ARRAY_SIZE(u8500_i2c0_resources),
|
||||
};
|
||||
|
||||
static struct resource u8500_i2c4_resources[] = {
|
||||
[0] = {
|
||||
.start = U8500_I2C4_BASE,
|
||||
.end = U8500_I2C4_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = IRQ_DB8500_I2C4,
|
||||
.end = IRQ_DB8500_I2C4,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
struct platform_device u8500_i2c4_device = {
|
||||
.name = "nmk-i2c",
|
||||
.id = 4,
|
||||
.resource = u8500_i2c4_resources,
|
||||
.num_resources = ARRAY_SIZE(u8500_i2c4_resources),
|
||||
};
|
||||
|
||||
/*
|
||||
* SD/MMC
|
||||
*/
|
||||
|
||||
struct amba_device u8500_sdi0_device = {
|
||||
.dev = {
|
||||
.init_name = "sdi0",
|
||||
},
|
||||
.res = {
|
||||
.start = U8500_SDI0_BASE,
|
||||
.end = U8500_SDI0_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
.irq = {IRQ_DB8500_SDMMC0, NO_IRQ},
|
||||
};
|
||||
|
||||
struct amba_device u8500_sdi1_device = {
|
||||
.dev = {
|
||||
.init_name = "sdi1",
|
||||
},
|
||||
.res = {
|
||||
.start = U8500_SDI1_BASE,
|
||||
.end = U8500_SDI1_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
.irq = {IRQ_DB8500_SDMMC1, NO_IRQ},
|
||||
};
|
||||
|
||||
struct amba_device u8500_sdi2_device = {
|
||||
.dev = {
|
||||
.init_name = "sdi2",
|
||||
},
|
||||
.res = {
|
||||
.start = U8500_SDI2_BASE,
|
||||
.end = U8500_SDI2_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
.irq = {IRQ_DB8500_SDMMC2, NO_IRQ},
|
||||
};
|
||||
|
||||
struct amba_device u8500_sdi3_device = {
|
||||
.dev = {
|
||||
.init_name = "sdi3",
|
||||
},
|
||||
.res = {
|
||||
.start = U8500_SDI3_BASE,
|
||||
.end = U8500_SDI3_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
.irq = {IRQ_DB8500_SDMMC3, NO_IRQ},
|
||||
};
|
||||
|
||||
struct amba_device u8500_sdi4_device = {
|
||||
.dev = {
|
||||
.init_name = "sdi4",
|
||||
},
|
||||
.res = {
|
||||
.start = U8500_SDI4_BASE,
|
||||
.end = U8500_SDI4_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
.irq = {IRQ_DB8500_SDMMC4, NO_IRQ},
|
||||
};
|
||||
|
||||
struct amba_device u8500_sdi5_device = {
|
||||
.dev = {
|
||||
.init_name = "sdi5",
|
||||
},
|
||||
.res = {
|
||||
.start = U8500_SDI5_BASE,
|
||||
.end = U8500_SDI5_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
.irq = {IRQ_DB8500_SDMMC5, NO_IRQ},
|
||||
};
|
||||
|
||||
static struct resource dma40_resources[] = {
|
||||
[0] = {
|
||||
.start = U8500_DMA_BASE,
|
||||
@ -295,7 +128,7 @@ struct resource keypad_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
struct platform_device ux500_ske_keypad_device = {
|
||||
struct platform_device u8500_ske_keypad_device = {
|
||||
.name = "nmk-ske-keypad",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(keypad_resources),
|
||||
|
98
arch/arm/mach-ux500/devices-db8500.h
Normal file
98
arch/arm/mach-ux500/devices-db8500.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL), version 2.
|
||||
*/
|
||||
|
||||
#ifndef __DEVICES_DB8500_H
|
||||
#define __DEVICES_DB8500_H
|
||||
|
||||
#include "devices-common.h"
|
||||
|
||||
struct ske_keypad_platform_data;
|
||||
struct pl022_ssp_controller;
|
||||
|
||||
static inline struct platform_device *
|
||||
db8500_add_ske_keypad(struct ske_keypad_platform_data *pdata)
|
||||
{
|
||||
return dbx500_add_platform_device_4k1irq("nmk-ske-keypad", -1,
|
||||
U8500_SKE_BASE,
|
||||
IRQ_DB8500_KB, pdata);
|
||||
}
|
||||
|
||||
static inline struct amba_device *
|
||||
db8500_add_ssp(const char *name, resource_size_t base, int irq,
|
||||
struct pl022_ssp_controller *pdata)
|
||||
{
|
||||
return dbx500_add_amba_device(name, base, irq, pdata, SSP_PER_ID);
|
||||
}
|
||||
|
||||
|
||||
#define db8500_add_i2c0(pdata) \
|
||||
dbx500_add_i2c(0, U8500_I2C0_BASE, IRQ_DB8500_I2C0, pdata)
|
||||
#define db8500_add_i2c1(pdata) \
|
||||
dbx500_add_i2c(1, U8500_I2C1_BASE, IRQ_DB8500_I2C1, pdata)
|
||||
#define db8500_add_i2c2(pdata) \
|
||||
dbx500_add_i2c(2, U8500_I2C2_BASE, IRQ_DB8500_I2C2, pdata)
|
||||
#define db8500_add_i2c3(pdata) \
|
||||
dbx500_add_i2c(3, U8500_I2C3_BASE, IRQ_DB8500_I2C3, pdata)
|
||||
#define db8500_add_i2c4(pdata) \
|
||||
dbx500_add_i2c(4, U8500_I2C4_BASE, IRQ_DB8500_I2C4, pdata)
|
||||
|
||||
#define db8500_add_msp0_i2s(pdata) \
|
||||
dbx500_add_msp_i2s(0, U8500_MSP0_BASE, IRQ_DB8500_MSP0, pdata)
|
||||
#define db8500_add_msp1_i2s(pdata) \
|
||||
dbx500_add_msp_i2s(1, U8500_MSP1_BASE, IRQ_DB8500_MSP1, pdata)
|
||||
#define db8500_add_msp2_i2s(pdata) \
|
||||
dbx500_add_msp_i2s(2, U8500_MSP2_BASE, IRQ_DB8500_MSP2, pdata)
|
||||
#define db8500_add_msp3_i2s(pdata) \
|
||||
dbx500_add_msp_i2s(3, U8500_MSP3_BASE, IRQ_DB8500_MSP1, pdata)
|
||||
|
||||
#define db8500_add_msp0_spi(pdata) \
|
||||
dbx500_add_msp_spi("msp0", U8500_MSP0_BASE, IRQ_DB8500_MSP0, pdata)
|
||||
#define db8500_add_msp1_spi(pdata) \
|
||||
dbx500_add_msp_spi("msp1", U8500_MSP1_BASE, IRQ_DB8500_MSP1, pdata)
|
||||
#define db8500_add_msp2_spi(pdata) \
|
||||
dbx500_add_msp_spi("msp2", U8500_MSP2_BASE, IRQ_DB8500_MSP2, pdata)
|
||||
#define db8500_add_msp3_spi(pdata) \
|
||||
dbx500_add_msp_spi("msp3", U8500_MSP3_BASE, IRQ_DB8500_MSP1, pdata)
|
||||
|
||||
#define db8500_add_rtc() \
|
||||
dbx500_add_rtc(U8500_RTC_BASE, IRQ_DB8500_RTC);
|
||||
|
||||
#define db8500_add_sdi0(pdata) \
|
||||
dbx500_add_sdi("sdi0", U8500_SDI0_BASE, IRQ_DB8500_SDMMC0, pdata)
|
||||
#define db8500_add_sdi1(pdata) \
|
||||
dbx500_add_sdi("sdi1", U8500_SDI1_BASE, IRQ_DB8500_SDMMC1, pdata)
|
||||
#define db8500_add_sdi2(pdata) \
|
||||
dbx500_add_sdi("sdi2", U8500_SDI2_BASE, IRQ_DB8500_SDMMC2, pdata)
|
||||
#define db8500_add_sdi3(pdata) \
|
||||
dbx500_add_sdi("sdi3", U8500_SDI3_BASE, IRQ_DB8500_SDMMC3, pdata)
|
||||
#define db8500_add_sdi4(pdata) \
|
||||
dbx500_add_sdi("sdi4", U8500_SDI4_BASE, IRQ_DB8500_SDMMC4, pdata)
|
||||
#define db8500_add_sdi5(pdata) \
|
||||
dbx500_add_sdi("sdi5", U8500_SDI5_BASE, IRQ_DB8500_SDMMC5, pdata)
|
||||
|
||||
#define db8500_add_ssp0(pdata) \
|
||||
db8500_add_ssp("ssp0", U8500_SSP0_BASE, IRQ_DB8500_SSP0, pdata)
|
||||
#define db8500_add_ssp1(pdata) \
|
||||
db8500_add_ssp("ssp1", U8500_SSP1_BASE, IRQ_DB8500_SSP1, pdata)
|
||||
|
||||
#define db8500_add_spi0(pdata) \
|
||||
dbx500_add_spi("spi0", U8500_SPI0_BASE, IRQ_DB8500_SPI0, pdata)
|
||||
#define db8500_add_spi1(pdata) \
|
||||
dbx500_add_spi("spi1", U8500_SPI1_BASE, IRQ_DB8500_SPI1, pdata)
|
||||
#define db8500_add_spi2(pdata) \
|
||||
dbx500_add_spi("spi2", U8500_SPI2_BASE, IRQ_DB8500_SPI2, pdata)
|
||||
#define db8500_add_spi3(pdata) \
|
||||
dbx500_add_spi("spi3", U8500_SPI3_BASE, IRQ_DB8500_SPI3, pdata)
|
||||
|
||||
#define db8500_add_uart0() \
|
||||
dbx500_add_uart("uart0", U8500_UART0_BASE, IRQ_DB8500_UART0)
|
||||
#define db8500_add_uart1() \
|
||||
dbx500_add_uart("uart1", U8500_UART1_BASE, IRQ_DB8500_UART1)
|
||||
#define db8500_add_uart2() \
|
||||
dbx500_add_uart("uart2", U8500_UART2_BASE, IRQ_DB8500_UART2)
|
||||
|
||||
#endif
|
@ -14,69 +14,6 @@
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/setup.h>
|
||||
|
||||
#define __MEM_4K_RESOURCE(x) \
|
||||
.res = {.start = (x), .end = (x) + SZ_4K - 1, .flags = IORESOURCE_MEM}
|
||||
|
||||
struct amba_device ux500_pl031_device = {
|
||||
.dev = {
|
||||
.init_name = "pl031",
|
||||
},
|
||||
.res = {
|
||||
.start = UX500_RTC_BASE,
|
||||
.end = UX500_RTC_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
.irq = {IRQ_RTC_RTT, NO_IRQ},
|
||||
};
|
||||
|
||||
struct amba_device ux500_uart0_device = {
|
||||
.dev = { .init_name = "uart0" },
|
||||
__MEM_4K_RESOURCE(UX500_UART0_BASE),
|
||||
.irq = {IRQ_UART0, NO_IRQ},
|
||||
};
|
||||
|
||||
struct amba_device ux500_uart1_device = {
|
||||
.dev = { .init_name = "uart1" },
|
||||
__MEM_4K_RESOURCE(UX500_UART1_BASE),
|
||||
.irq = {IRQ_UART1, NO_IRQ},
|
||||
};
|
||||
|
||||
struct amba_device ux500_uart2_device = {
|
||||
.dev = { .init_name = "uart2" },
|
||||
__MEM_4K_RESOURCE(UX500_UART2_BASE),
|
||||
.irq = {IRQ_UART2, NO_IRQ},
|
||||
};
|
||||
|
||||
#define UX500_I2C_RESOURCES(id, size) \
|
||||
static struct resource ux500_i2c##id##_resources[] = { \
|
||||
[0] = { \
|
||||
.start = UX500_I2C##id##_BASE, \
|
||||
.end = UX500_I2C##id##_BASE + size - 1, \
|
||||
.flags = IORESOURCE_MEM, \
|
||||
}, \
|
||||
[1] = { \
|
||||
.start = IRQ_I2C##id, \
|
||||
.end = IRQ_I2C##id, \
|
||||
.flags = IORESOURCE_IRQ \
|
||||
} \
|
||||
}
|
||||
|
||||
UX500_I2C_RESOURCES(1, SZ_4K);
|
||||
UX500_I2C_RESOURCES(2, SZ_4K);
|
||||
UX500_I2C_RESOURCES(3, SZ_4K);
|
||||
|
||||
#define UX500_I2C_PDEVICE(cid) \
|
||||
struct platform_device ux500_i2c##cid##_device = { \
|
||||
.name = "nmk-i2c", \
|
||||
.id = cid, \
|
||||
.num_resources = 2, \
|
||||
.resource = ux500_i2c##cid##_resources, \
|
||||
}
|
||||
|
||||
UX500_I2C_PDEVICE(1);
|
||||
UX500_I2C_PDEVICE(2);
|
||||
UX500_I2C_PDEVICE(3);
|
||||
|
||||
void __init amba_add_devices(struct amba_device *devs[], int num)
|
||||
{
|
||||
int i;
|
||||
|
120
arch/arm/mach-ux500/dma-db5500.c
Normal file
120
arch/arm/mach-ux500/dma-db5500.c
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson
|
||||
* Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
|
||||
* Author: Rabin Vincent <rabinv.vincent@stericsson.com> for ST-Ericsson
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <plat/ste_dma40.h>
|
||||
#include <mach/setup.h>
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#include "ste-dma40-db5500.h"
|
||||
|
||||
static struct resource dma40_resources[] = {
|
||||
[0] = {
|
||||
.start = U5500_DMA_BASE,
|
||||
.end = U5500_DMA_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
.name = "base",
|
||||
},
|
||||
[1] = {
|
||||
.start = U5500_DMA_LCPA_BASE,
|
||||
.end = U5500_DMA_LCPA_BASE + 2 * SZ_1K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
.name = "lcpa",
|
||||
},
|
||||
[2] = {
|
||||
.start = IRQ_DB5500_DMA,
|
||||
.end = IRQ_DB5500_DMA,
|
||||
.flags = IORESOURCE_IRQ
|
||||
}
|
||||
};
|
||||
|
||||
/* Default configuration for physical memcpy */
|
||||
static struct stedma40_chan_cfg dma40_memcpy_conf_phy = {
|
||||
.mode = STEDMA40_MODE_PHYSICAL,
|
||||
.dir = STEDMA40_MEM_TO_MEM,
|
||||
|
||||
.src_info.data_width = STEDMA40_BYTE_WIDTH,
|
||||
.src_info.psize = STEDMA40_PSIZE_PHY_1,
|
||||
.src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
|
||||
|
||||
.dst_info.data_width = STEDMA40_BYTE_WIDTH,
|
||||
.dst_info.psize = STEDMA40_PSIZE_PHY_1,
|
||||
.dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
|
||||
};
|
||||
|
||||
/* Default configuration for logical memcpy */
|
||||
static struct stedma40_chan_cfg dma40_memcpy_conf_log = {
|
||||
.dir = STEDMA40_MEM_TO_MEM,
|
||||
|
||||
.src_info.data_width = STEDMA40_BYTE_WIDTH,
|
||||
.src_info.psize = STEDMA40_PSIZE_LOG_1,
|
||||
.src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
|
||||
|
||||
.dst_info.data_width = STEDMA40_BYTE_WIDTH,
|
||||
.dst_info.psize = STEDMA40_PSIZE_LOG_1,
|
||||
.dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Mapping between soruce event lines and physical device address This was
|
||||
* created assuming that the event line is tied to a device and therefore the
|
||||
* address is constant, however this is not true for at least USB, and the
|
||||
* values are just placeholders for USB. This table is preserved and used for
|
||||
* now.
|
||||
*/
|
||||
static const dma_addr_t dma40_rx_map[DB5500_DMA_NR_DEV] = {
|
||||
[DB5500_DMA_DEV24_SDMMC0_RX] = -1,
|
||||
};
|
||||
|
||||
/* Mapping between destination event lines and physical device address */
|
||||
static const dma_addr_t dma40_tx_map[DB5500_DMA_NR_DEV] = {
|
||||
[DB5500_DMA_DEV24_SDMMC0_TX] = -1,
|
||||
};
|
||||
|
||||
static int dma40_memcpy_event[] = {
|
||||
DB5500_DMA_MEMCPY_TX_1,
|
||||
DB5500_DMA_MEMCPY_TX_2,
|
||||
DB5500_DMA_MEMCPY_TX_3,
|
||||
DB5500_DMA_MEMCPY_TX_4,
|
||||
DB5500_DMA_MEMCPY_TX_5,
|
||||
};
|
||||
|
||||
static struct stedma40_platform_data dma40_plat_data = {
|
||||
.dev_len = ARRAY_SIZE(dma40_rx_map),
|
||||
.dev_rx = dma40_rx_map,
|
||||
.dev_tx = dma40_tx_map,
|
||||
.memcpy = dma40_memcpy_event,
|
||||
.memcpy_len = ARRAY_SIZE(dma40_memcpy_event),
|
||||
.memcpy_conf_phy = &dma40_memcpy_conf_phy,
|
||||
.memcpy_conf_log = &dma40_memcpy_conf_log,
|
||||
.disabled_channels = {-1},
|
||||
};
|
||||
|
||||
static struct platform_device dma40_device = {
|
||||
.dev = {
|
||||
.platform_data = &dma40_plat_data,
|
||||
},
|
||||
.name = "dma40",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(dma40_resources),
|
||||
.resource = dma40_resources
|
||||
};
|
||||
|
||||
void __init db5500_dma_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_device_register(&dma40_device);
|
||||
if (ret)
|
||||
dev_err(&dma40_device.dev, "unable to register device: %d\n", ret);
|
||||
|
||||
}
|
@ -114,4 +114,8 @@
|
||||
#define U5500_MBOX2_LOCAL_START (U5500_MBOX_BASE + 0x20)
|
||||
#define U5500_MBOX2_LOCAL_END (U5500_MBOX_BASE + 0x3F)
|
||||
|
||||
#define U5500_ESRAM_BASE 0x40000000
|
||||
#define U5500_ESRAM_DMA_LCPA_OFFSET 0x10000
|
||||
#define U5500_DMA_LCPA_BASE (U5500_ESRAM_BASE + U5500_ESRAM_DMA_LCPA_OFFSET)
|
||||
|
||||
#endif
|
||||
|
@ -92,7 +92,8 @@
|
||||
#define U8500_SCR_BASE (U8500_PER4_BASE + 0x05000)
|
||||
#define U8500_DMC_BASE (U8500_PER4_BASE + 0x06000)
|
||||
#define U8500_PRCMU_BASE (U8500_PER4_BASE + 0x07000)
|
||||
#define U8500_PRCMU_TCDM_BASE (U8500_PER4_BASE + 0x0f000)
|
||||
#define U8500_PRCMU_TCDM_BASE_V1 (U8500_PER4_BASE + 0x0f000)
|
||||
#define U8500_PRCMU_TCDM_BASE (U8500_PER4_BASE + 0x68000)
|
||||
|
||||
/* per3 base addresses */
|
||||
#define U8500_FSMC_BASE (U8500_PER3_BASE + 0x0000)
|
||||
|
@ -14,27 +14,10 @@ extern struct platform_device u5500_gpio_devs[];
|
||||
extern struct platform_device u8500_gpio_devs[];
|
||||
|
||||
extern struct amba_device ux500_pl031_device;
|
||||
extern struct amba_device u8500_ssp0_device;
|
||||
extern struct amba_device ux500_uart0_device;
|
||||
extern struct amba_device ux500_uart1_device;
|
||||
extern struct amba_device ux500_uart2_device;
|
||||
|
||||
extern struct platform_device ux500_i2c1_device;
|
||||
extern struct platform_device ux500_i2c2_device;
|
||||
extern struct platform_device ux500_i2c3_device;
|
||||
|
||||
extern struct platform_device u8500_i2c0_device;
|
||||
extern struct platform_device u8500_i2c4_device;
|
||||
extern struct platform_device u8500_dma40_device;
|
||||
extern struct platform_device ux500_ske_keypad_device;
|
||||
|
||||
extern struct amba_device u8500_sdi0_device;
|
||||
extern struct amba_device u8500_sdi1_device;
|
||||
extern struct amba_device u8500_sdi2_device;
|
||||
extern struct amba_device u8500_sdi3_device;
|
||||
extern struct amba_device u8500_sdi4_device;
|
||||
extern struct amba_device u8500_sdi5_device;
|
||||
|
||||
void dma40_u8500ed_fixup(void);
|
||||
|
||||
#endif
|
||||
|
@ -9,42 +9,4 @@
|
||||
|
||||
#include <plat/gpio.h>
|
||||
|
||||
#define __GPIO_RESOURCE(soc, block) \
|
||||
{ \
|
||||
.start = soc##_GPIOBANK##block##_BASE, \
|
||||
.end = soc##_GPIOBANK##block##_BASE + 127, \
|
||||
.flags = IORESOURCE_MEM, \
|
||||
}, \
|
||||
{ \
|
||||
.start = IRQ_GPIO##block, \
|
||||
.end = IRQ_GPIO##block, \
|
||||
.flags = IORESOURCE_IRQ, \
|
||||
}
|
||||
|
||||
#define __GPIO_DEVICE(soc, block) \
|
||||
{ \
|
||||
.name = "gpio", \
|
||||
.id = block, \
|
||||
.num_resources = 2, \
|
||||
.resource = &soc##_gpio_resources[block * 2], \
|
||||
.dev = { \
|
||||
.platform_data = &soc##_gpio_data[block], \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define GPIO_DATA(_name, first) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.first_gpio = first, \
|
||||
.first_irq = NOMADIK_GPIO_TO_IRQ(first), \
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UX500_SOC_DB8500
|
||||
#define GPIO_RESOURCE(block) __GPIO_RESOURCE(U8500, block)
|
||||
#define GPIO_DEVICE(block) __GPIO_DEVICE(u8500, block)
|
||||
#elif defined(CONFIG_UX500_SOC_DB5500)
|
||||
#define GPIO_RESOURCE(block) __GPIO_RESOURCE(U5500, block)
|
||||
#define GPIO_DEVICE(block) __GPIO_DEVICE(u5500, block)
|
||||
#endif
|
||||
|
||||
#endif /* __ASM_ARCH_GPIO_H */
|
||||
|
@ -142,6 +142,8 @@ static inline bool cpu_is_u5500(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __MACH_HARDWARE_H */
|
||||
|
@ -8,12 +8,36 @@
|
||||
#ifndef __MACH_IRQS_BOARD_MOP500_H
|
||||
#define __MACH_IRQS_BOARD_MOP500_H
|
||||
|
||||
#define AB8500_NR_IRQS 104
|
||||
/* Number of AB8500 irqs is taken from header file */
|
||||
#include <linux/mfd/ab8500.h>
|
||||
|
||||
#define MOP500_AB8500_IRQ_BASE IRQ_BOARD_START
|
||||
#define MOP500_AB8500_IRQ_END (MOP500_AB8500_IRQ_BASE \
|
||||
+ AB8500_NR_IRQS)
|
||||
#define MOP500_IRQ_END MOP500_AB8500_IRQ_END
|
||||
|
||||
/* TC35892 */
|
||||
#define TC35892_NR_INTERNAL_IRQS 8
|
||||
#define TC35892_INT_GPIO(x) (TC35892_NR_INTERNAL_IRQS + (x))
|
||||
#define TC35892_NR_GPIOS 24
|
||||
#define TC35892_NR_IRQS TC35892_INT_GPIO(TC35892_NR_GPIOS)
|
||||
|
||||
#define MOP500_EGPIO_NR_IRQS TC35892_NR_IRQS
|
||||
|
||||
#define MOP500_EGPIO_IRQ_BASE MOP500_AB8500_IRQ_END
|
||||
#define MOP500_EGPIO_IRQ_END (MOP500_EGPIO_IRQ_BASE \
|
||||
+ MOP500_EGPIO_NR_IRQS)
|
||||
/* STMPE1601 irqs */
|
||||
#define STMPE_NR_INTERNAL_IRQS 9
|
||||
#define STMPE_INT_GPIO(x) (STMPE_NR_INTERNAL_IRQS + (x))
|
||||
#define STMPE_NR_GPIOS 24
|
||||
#define STMPE_NR_IRQS STMPE_INT_GPIO(STMPE_NR_GPIOS)
|
||||
|
||||
#define MOP500_STMPE1601_IRQBASE MOP500_EGPIO_IRQ_END
|
||||
#define MOP500_STMPE1601_IRQ(x) (MOP500_STMPE1601_IRQBASE + (x))
|
||||
|
||||
#define MOP500_NR_IRQS MOP500_STMPE1601_IRQ(STMPE_NR_INTERNAL_IRQS)
|
||||
|
||||
#define MOP500_IRQ_END MOP500_NR_IRQS
|
||||
|
||||
#if MOP500_IRQ_END > IRQ_BOARD_END
|
||||
#undef IRQ_BOARD_END
|
||||
|
@ -21,50 +21,6 @@
|
||||
|
||||
/* Interrupt numbers generic for shared peripheral */
|
||||
#define IRQ_MTU0 (IRQ_SHPI_START + 4)
|
||||
#define IRQ_SPI2 (IRQ_SHPI_START + 6)
|
||||
#define IRQ_SPI0 (IRQ_SHPI_START + 8)
|
||||
#define IRQ_UART0 (IRQ_SHPI_START + 11)
|
||||
#define IRQ_I2C3 (IRQ_SHPI_START + 12)
|
||||
#define IRQ_SSP0 (IRQ_SHPI_START + 14)
|
||||
#define IRQ_MTU1 (IRQ_SHPI_START + 17)
|
||||
#define IRQ_RTC_RTT (IRQ_SHPI_START + 18)
|
||||
#define IRQ_UART1 (IRQ_SHPI_START + 19)
|
||||
#define IRQ_I2C0 (IRQ_SHPI_START + 21)
|
||||
#define IRQ_I2C1 (IRQ_SHPI_START + 22)
|
||||
#define IRQ_USBOTG (IRQ_SHPI_START + 23)
|
||||
#define IRQ_DMA (IRQ_SHPI_START + 25)
|
||||
#define IRQ_UART2 (IRQ_SHPI_START + 26)
|
||||
#define IRQ_HSIR_EXCEP (IRQ_SHPI_START + 29)
|
||||
#define IRQ_MSP0 (IRQ_SHPI_START + 31)
|
||||
#define IRQ_HSIR_CH0_OVRRUN (IRQ_SHPI_START + 32)
|
||||
#define IRQ_HSIR_CH1_OVRRUN (IRQ_SHPI_START + 33)
|
||||
#define IRQ_HSIR_CH2_OVRRUN (IRQ_SHPI_START + 34)
|
||||
#define IRQ_HSIR_CH3_OVRRUN (IRQ_SHPI_START + 35)
|
||||
#define IRQ_AB8500 (IRQ_SHPI_START + 40)
|
||||
#define IRQ_PRCMU (IRQ_SHPI_START + 47)
|
||||
#define IRQ_DISP (IRQ_SHPI_START + 48)
|
||||
#define IRQ_SiPI3 (IRQ_SHPI_START + 49)
|
||||
#define IRQ_I2C4 (IRQ_SHPI_START + 51)
|
||||
#define IRQ_SSP1 (IRQ_SHPI_START + 52)
|
||||
#define IRQ_I2C2 (IRQ_SHPI_START + 55)
|
||||
#define IRQ_SDMMC0 (IRQ_SHPI_START + 60)
|
||||
#define IRQ_MSP1 (IRQ_SHPI_START + 62)
|
||||
#define IRQ_SPI1 (IRQ_SHPI_START + 96)
|
||||
#define IRQ_MSP2 (IRQ_SHPI_START + 98)
|
||||
#define IRQ_SDMMC4 (IRQ_SHPI_START + 99)
|
||||
#define IRQ_HSIRD0 (IRQ_SHPI_START + 104)
|
||||
#define IRQ_HSIRD1 (IRQ_SHPI_START + 105)
|
||||
#define IRQ_HSITD0 (IRQ_SHPI_START + 106)
|
||||
#define IRQ_HSITD1 (IRQ_SHPI_START + 107)
|
||||
#define IRQ_GPIO0 (IRQ_SHPI_START + 119)
|
||||
#define IRQ_GPIO1 (IRQ_SHPI_START + 120)
|
||||
#define IRQ_GPIO2 (IRQ_SHPI_START + 121)
|
||||
#define IRQ_GPIO3 (IRQ_SHPI_START + 122)
|
||||
#define IRQ_GPIO4 (IRQ_SHPI_START + 123)
|
||||
#define IRQ_GPIO5 (IRQ_SHPI_START + 124)
|
||||
#define IRQ_GPIO6 (IRQ_SHPI_START + 125)
|
||||
#define IRQ_GPIO7 (IRQ_SHPI_START + 126)
|
||||
#define IRQ_GPIO8 (IRQ_SHPI_START + 127)
|
||||
|
||||
/* There are 128 shared peripheral interrupts assigned to
|
||||
* INTID[160:32]. The first 32 interrupts are reserved.
|
||||
|
30
arch/arm/mach-ux500/include/mach/prcmu-defs.h
Normal file
30
arch/arm/mach-ux500/include/mach/prcmu-defs.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2009
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com>
|
||||
* Author: Martin Persson <martin.persson@stericsson.com>
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
*
|
||||
* PRCM Unit definitions
|
||||
*/
|
||||
|
||||
#ifndef __MACH_PRCMU_DEFS_H
|
||||
#define __MACH_PRCMU_DEFS_H
|
||||
|
||||
enum prcmu_cpu_opp {
|
||||
CPU_OPP_INIT = 0x00,
|
||||
CPU_OPP_NO_CHANGE = 0x01,
|
||||
CPU_OPP_100 = 0x02,
|
||||
CPU_OPP_50 = 0x03,
|
||||
CPU_OPP_MAX = 0x04,
|
||||
CPU_OPP_EXT_CLK = 0x07
|
||||
};
|
||||
enum prcmu_ape_opp {
|
||||
APE_OPP_NO_CHANGE = 0x00,
|
||||
APE_OPP_100 = 0x02,
|
||||
APE_OPP_50 = 0x03,
|
||||
};
|
||||
|
||||
#endif /* __MACH_PRCMU_DEFS_H */
|
@ -1,10 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2009 ST-Ericsson SA
|
||||
* Copyright (C) STMicroelectronics 2009
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
* Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com>
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
*
|
||||
* PRCM Unit registers
|
||||
*/
|
||||
|
||||
#ifndef __MACH_PRCMU_REGS_H
|
||||
#define __MACH_PRCMU_REGS_H
|
||||
|
||||
@ -88,4 +93,4 @@
|
||||
/* Miscellaneous unit registers */
|
||||
#define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324)
|
||||
|
||||
#endif /* __MACH_PRCMU__REGS_H */
|
||||
#endif /* __MACH_PRCMU_REGS_H */
|
||||
|
@ -2,14 +2,27 @@
|
||||
* Copyright (C) STMicroelectronics 2009
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com>
|
||||
* Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
*
|
||||
* PRCMU f/w APIs
|
||||
* PRCM Unit f/w API
|
||||
*/
|
||||
#ifndef __MACH_PRCMU_H
|
||||
#define __MACH_PRCMU_H
|
||||
#include <mach/prcmu-defs.h>
|
||||
|
||||
void __init prcmu_early_init(void);
|
||||
int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
|
||||
int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size);
|
||||
int prcmu_set_ape_opp(enum prcmu_ape_opp opp);
|
||||
int prcmu_set_cpu_opp(enum prcmu_cpu_opp opp);
|
||||
int prcmu_set_ape_cpu_opps(enum prcmu_ape_opp ape_opp,
|
||||
enum prcmu_cpu_opp cpu_opp);
|
||||
int prcmu_get_ape_opp(void);
|
||||
int prcmu_get_cpu_opp(void);
|
||||
bool prcmu_has_arm_maxopp(void);
|
||||
|
||||
#endif /* __MACH_PRCMU_H */
|
||||
|
@ -18,14 +18,19 @@ extern void __init ux500_map_io(void);
|
||||
extern void __init u5500_map_io(void);
|
||||
extern void __init u8500_map_io(void);
|
||||
|
||||
extern void __init ux500_init_devices(void);
|
||||
extern void __init u5500_init_devices(void);
|
||||
extern void __init u8500_init_devices(void);
|
||||
|
||||
extern void __init ux500_init_irq(void);
|
||||
|
||||
extern void __init u5500_sdi_init(void);
|
||||
|
||||
extern void __init db5500_dma_init(void);
|
||||
|
||||
/* We re-use nomadik_timer for this platform */
|
||||
extern void nmdk_timer_init(void);
|
||||
|
||||
struct amba_device;
|
||||
extern void __init amba_add_devices(struct amba_device *devs[], int num);
|
||||
|
||||
struct sys_timer;
|
||||
|
@ -19,38 +19,43 @@
|
||||
#define __ASM_ARCH_UNCOMPRESS_H
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/amba/serial.h>
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#define U8500_UART_DR 0x80007000
|
||||
#define U8500_UART_LCRH 0x8000702c
|
||||
#define U8500_UART_CR 0x80007030
|
||||
#define U8500_UART_FR 0x80007018
|
||||
static u32 ux500_uart_base;
|
||||
|
||||
static void putc(const char c)
|
||||
{
|
||||
/* Do nothing if the UART is not enabled. */
|
||||
if (!(__raw_readb(U8500_UART_CR) & 0x1))
|
||||
if (!(__raw_readb(ux500_uart_base + UART011_CR) & 0x1))
|
||||
return;
|
||||
|
||||
if (c == '\n')
|
||||
putc('\r');
|
||||
|
||||
while (__raw_readb(U8500_UART_FR) & (1 << 5))
|
||||
while (__raw_readb(ux500_uart_base + UART01x_FR) & (1 << 5))
|
||||
barrier();
|
||||
__raw_writeb(c, U8500_UART_DR);
|
||||
__raw_writeb(c, ux500_uart_base + UART01x_DR);
|
||||
}
|
||||
|
||||
static void flush(void)
|
||||
{
|
||||
if (!(__raw_readb(U8500_UART_CR) & 0x1))
|
||||
if (!(__raw_readb(ux500_uart_base + UART011_CR) & 0x1))
|
||||
return;
|
||||
while (__raw_readb(U8500_UART_FR) & (1 << 3))
|
||||
while (__raw_readb(ux500_uart_base + UART01x_FR) & (1 << 3))
|
||||
barrier();
|
||||
}
|
||||
|
||||
static inline void arch_decomp_setup(void)
|
||||
{
|
||||
if (machine_is_u8500())
|
||||
ux500_uart_base = U8500_UART2_BASE;
|
||||
else if (machine_is_u5500())
|
||||
ux500_uart_base = U5500_UART0_BASE;
|
||||
else /* not much can be done to help here */
|
||||
ux500_uart_base = U8500_UART2_BASE;
|
||||
}
|
||||
|
||||
#define arch_decomp_wdog() /* nothing to do here */
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/completion.h>
|
||||
#include <mach/mbox.h>
|
||||
#include <mach/mbox-db5500.h>
|
||||
|
||||
#define MBOX_NAME "mbox"
|
||||
|
@ -26,7 +26,7 @@
|
||||
* control for which core is the next to come out of the secondary
|
||||
* boot "holding pen"
|
||||
*/
|
||||
volatile int __cpuinitdata pen_release = -1;
|
||||
volatile int pen_release = -1;
|
||||
|
||||
static unsigned int __init get_core_count(void)
|
||||
{
|
||||
|
@ -1,10 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) ST Ericsson SA 2010
|
||||
* Copyright (C) STMicroelectronics 2009
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
* Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com>
|
||||
* Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
|
||||
*
|
||||
* U8500 PRCMU driver.
|
||||
* U8500 PRCM Unit interface driver
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -19,11 +23,26 @@
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/prcmu-regs.h>
|
||||
#include <mach/prcmu-defs.h>
|
||||
|
||||
#define PRCMU_TCDM_BASE __io_address(U8500_PRCMU_TCDM_BASE)
|
||||
/* Global var to runtime determine TCDM base for v2 or v1 */
|
||||
static __iomem void *tcdm_base;
|
||||
|
||||
#define REQ_MB5 (PRCMU_TCDM_BASE + 0xE44)
|
||||
#define ACK_MB5 (PRCMU_TCDM_BASE + 0xDF4)
|
||||
#define _MBOX_HEADER (tcdm_base + 0xFE8)
|
||||
#define MBOX_HEADER_REQ_MB0 (_MBOX_HEADER + 0x0)
|
||||
|
||||
#define REQ_MB1 (tcdm_base + 0xFD0)
|
||||
#define REQ_MB5 (tcdm_base + 0xE44)
|
||||
|
||||
#define REQ_MB1_ARMOPP (REQ_MB1 + 0x0)
|
||||
#define REQ_MB1_APEOPP (REQ_MB1 + 0x1)
|
||||
#define REQ_MB1_BOOSTOPP (REQ_MB1 + 0x2)
|
||||
|
||||
#define ACK_MB1 (tcdm_base + 0xE04)
|
||||
#define ACK_MB5 (tcdm_base + 0xDF4)
|
||||
|
||||
#define ACK_MB1_CURR_ARMOPP (ACK_MB1 + 0x0)
|
||||
#define ACK_MB1_CURR_APEOPP (ACK_MB1 + 0x1)
|
||||
|
||||
#define REQ_MB5_I2C_SLAVE_OP (REQ_MB5)
|
||||
#define REQ_MB5_I2C_HW_BITS (REQ_MB5 + 1)
|
||||
@ -33,10 +52,33 @@
|
||||
#define ACK_MB5_I2C_STATUS (ACK_MB5 + 1)
|
||||
#define ACK_MB5_I2C_VAL (ACK_MB5 + 3)
|
||||
|
||||
#define I2C_WRITE(slave) ((slave) << 1)
|
||||
#define I2C_READ(slave) (((slave) << 1) | BIT(0))
|
||||
#define PRCM_AVS_VARM_MAX_OPP (tcdm_base + 0x2E4)
|
||||
#define PRCM_AVS_ISMODEENABLE 7
|
||||
#define PRCM_AVS_ISMODEENABLE_MASK (1 << PRCM_AVS_ISMODEENABLE)
|
||||
|
||||
#define I2C_WRITE(slave) \
|
||||
(((slave) << 1) | (cpu_is_u8500v2() ? BIT(6) : 0))
|
||||
#define I2C_READ(slave) \
|
||||
(((slave) << 1) | (cpu_is_u8500v2() ? BIT(6) : 0) | BIT(0))
|
||||
#define I2C_STOP_EN BIT(3)
|
||||
|
||||
enum mb1_h {
|
||||
MB1H_ARM_OPP = 1,
|
||||
MB1H_APE_OPP,
|
||||
MB1H_ARM_APE_OPP,
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct mutex lock;
|
||||
struct completion work;
|
||||
struct {
|
||||
u8 arm_opp;
|
||||
u8 ape_opp;
|
||||
u8 arm_status;
|
||||
u8 ape_status;
|
||||
} ack;
|
||||
} mb1_transfer;
|
||||
|
||||
enum ack_mb5_status {
|
||||
I2C_WR_OK = 0x01,
|
||||
I2C_RD_OK = 0x02,
|
||||
@ -145,6 +187,104 @@ unlock_and_return:
|
||||
}
|
||||
EXPORT_SYMBOL(prcmu_abb_write);
|
||||
|
||||
static int set_ape_cpu_opps(u8 header, enum prcmu_ape_opp ape_opp,
|
||||
enum prcmu_cpu_opp cpu_opp)
|
||||
{
|
||||
bool do_ape;
|
||||
bool do_arm;
|
||||
int err = 0;
|
||||
|
||||
do_ape = ((header == MB1H_APE_OPP) || (header == MB1H_ARM_APE_OPP));
|
||||
do_arm = ((header == MB1H_ARM_OPP) || (header == MB1H_ARM_APE_OPP));
|
||||
|
||||
mutex_lock(&mb1_transfer.lock);
|
||||
|
||||
while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
|
||||
cpu_relax();
|
||||
|
||||
writeb(0, MBOX_HEADER_REQ_MB0);
|
||||
writeb(cpu_opp, REQ_MB1_ARMOPP);
|
||||
writeb(ape_opp, REQ_MB1_APEOPP);
|
||||
writeb(0, REQ_MB1_BOOSTOPP);
|
||||
writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
|
||||
wait_for_completion(&mb1_transfer.work);
|
||||
if ((do_ape) && (mb1_transfer.ack.ape_status != 0))
|
||||
err = -EIO;
|
||||
if ((do_arm) && (mb1_transfer.ack.arm_status != 0))
|
||||
err = -EIO;
|
||||
|
||||
mutex_unlock(&mb1_transfer.lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* prcmu_set_ape_opp() - Set the OPP of the APE.
|
||||
* @opp: The OPP to set.
|
||||
*
|
||||
* This function sets the OPP of the APE.
|
||||
*/
|
||||
int prcmu_set_ape_opp(enum prcmu_ape_opp opp)
|
||||
{
|
||||
return set_ape_cpu_opps(MB1H_APE_OPP, opp, APE_OPP_NO_CHANGE);
|
||||
}
|
||||
EXPORT_SYMBOL(prcmu_set_ape_opp);
|
||||
|
||||
/**
|
||||
* prcmu_set_cpu_opp() - Set the OPP of the CPU.
|
||||
* @opp: The OPP to set.
|
||||
*
|
||||
* This function sets the OPP of the CPU.
|
||||
*/
|
||||
int prcmu_set_cpu_opp(enum prcmu_cpu_opp opp)
|
||||
{
|
||||
return set_ape_cpu_opps(MB1H_ARM_OPP, CPU_OPP_NO_CHANGE, opp);
|
||||
}
|
||||
EXPORT_SYMBOL(prcmu_set_cpu_opp);
|
||||
|
||||
/**
|
||||
* prcmu_set_ape_cpu_opps() - Set the OPPs of the APE and the CPU.
|
||||
* @ape_opp: The APE OPP to set.
|
||||
* @cpu_opp: The CPU OPP to set.
|
||||
*
|
||||
* This function sets the OPPs of the APE and the CPU.
|
||||
*/
|
||||
int prcmu_set_ape_cpu_opps(enum prcmu_ape_opp ape_opp,
|
||||
enum prcmu_cpu_opp cpu_opp)
|
||||
{
|
||||
return set_ape_cpu_opps(MB1H_ARM_APE_OPP, ape_opp, cpu_opp);
|
||||
}
|
||||
EXPORT_SYMBOL(prcmu_set_ape_cpu_opps);
|
||||
|
||||
/**
|
||||
* prcmu_get_ape_opp() - Get the OPP of the APE.
|
||||
*
|
||||
* This function gets the OPP of the APE.
|
||||
*/
|
||||
enum prcmu_ape_opp prcmu_get_ape_opp(void)
|
||||
{
|
||||
return readb(ACK_MB1_CURR_APEOPP);
|
||||
}
|
||||
EXPORT_SYMBOL(prcmu_get_ape_opp);
|
||||
|
||||
/**
|
||||
* prcmu_get_cpu_opp() - Get the OPP of the CPU.
|
||||
*
|
||||
* This function gets the OPP of the CPU. The OPP is specified in %%.
|
||||
* PRCMU_OPP_EXT is a special OPP value, not specified in %%.
|
||||
*/
|
||||
int prcmu_get_cpu_opp(void)
|
||||
{
|
||||
return readb(ACK_MB1_CURR_ARMOPP);
|
||||
}
|
||||
EXPORT_SYMBOL(prcmu_get_cpu_opp);
|
||||
|
||||
bool prcmu_has_arm_maxopp(void)
|
||||
{
|
||||
return (readb(PRCM_AVS_VARM_MAX_OPP) & PRCM_AVS_ISMODEENABLE_MASK)
|
||||
== PRCM_AVS_ISMODEENABLE_MASK;
|
||||
}
|
||||
|
||||
static void read_mailbox_0(void)
|
||||
{
|
||||
writel(MBOX_BIT(0), PRCM_ARM_IT1_CLEAR);
|
||||
@ -152,6 +292,9 @@ static void read_mailbox_0(void)
|
||||
|
||||
static void read_mailbox_1(void)
|
||||
{
|
||||
mb1_transfer.ack.arm_opp = readb(ACK_MB1_CURR_ARMOPP);
|
||||
mb1_transfer.ack.ape_opp = readb(ACK_MB1_CURR_APEOPP);
|
||||
complete(&mb1_transfer.work);
|
||||
writel(MBOX_BIT(1), PRCM_ARM_IT1_CLEAR);
|
||||
}
|
||||
|
||||
@ -217,15 +360,35 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void __init prcmu_early_init(void)
|
||||
{
|
||||
if (cpu_is_u8500v11() || cpu_is_u8500ed()) {
|
||||
tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE_V1);
|
||||
} else if (cpu_is_u8500v2()) {
|
||||
tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
|
||||
} else {
|
||||
pr_err("prcmu: Unsupported chip version\n");
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static int __init prcmu_init(void)
|
||||
{
|
||||
if (cpu_is_u8500ed()) {
|
||||
pr_err("prcmu: Unsupported chip version\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_init(&mb1_transfer.lock);
|
||||
init_completion(&mb1_transfer.work);
|
||||
mutex_init(&mb5_transfer.lock);
|
||||
init_completion(&mb5_transfer.work);
|
||||
|
||||
/* Clean up the mailbox interrupts after pre-kernel code. */
|
||||
writel((MBOX_BIT(NUM_MBOX) - 1), PRCM_ARM_IT1_CLEAR);
|
||||
|
||||
return request_irq(IRQ_PRCMU, prcmu_irq_handler, 0, "prcmu", NULL);
|
||||
return request_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler, 0,
|
||||
"prcmu", NULL);
|
||||
}
|
||||
|
||||
arch_initcall(prcmu_init);
|
||||
|
@ -119,7 +119,7 @@ static void __nmk_gpio_make_output(struct nmk_gpio_chip *nmk_chip,
|
||||
}
|
||||
|
||||
static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
|
||||
pin_cfg_t cfg)
|
||||
pin_cfg_t cfg, bool sleep)
|
||||
{
|
||||
static const char *afnames[] = {
|
||||
[NMK_GPIO_ALT_GPIO] = "GPIO",
|
||||
@ -145,11 +145,34 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
|
||||
int output = PIN_DIR(cfg);
|
||||
int val = PIN_VAL(cfg);
|
||||
|
||||
dev_dbg(nmk_chip->chip.dev, "pin %d: af %s, pull %s, slpm %s (%s%s)\n",
|
||||
pin, afnames[af], pullnames[pull], slpmnames[slpm],
|
||||
dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n",
|
||||
pin, cfg, afnames[af], pullnames[pull], slpmnames[slpm],
|
||||
output ? "output " : "input",
|
||||
output ? (val ? "high" : "low") : "");
|
||||
|
||||
if (sleep) {
|
||||
int slpm_pull = PIN_SLPM_PULL(cfg);
|
||||
int slpm_output = PIN_SLPM_DIR(cfg);
|
||||
int slpm_val = PIN_SLPM_VAL(cfg);
|
||||
|
||||
/*
|
||||
* The SLPM_* values are normal values + 1 to allow zero to
|
||||
* mean "same as normal".
|
||||
*/
|
||||
if (slpm_pull)
|
||||
pull = slpm_pull - 1;
|
||||
if (slpm_output)
|
||||
output = slpm_output - 1;
|
||||
if (slpm_val)
|
||||
val = slpm_val - 1;
|
||||
|
||||
dev_dbg(nmk_chip->chip.dev, "pin %d: sleep pull %s, dir %s, val %s\n",
|
||||
pin,
|
||||
slpm_pull ? pullnames[pull] : "same",
|
||||
slpm_output ? (output ? "output" : "input") : "same",
|
||||
slpm_val ? (val ? "high" : "low") : "same");
|
||||
}
|
||||
|
||||
if (output)
|
||||
__nmk_gpio_make_output(nmk_chip, offset, val);
|
||||
else {
|
||||
@ -175,7 +198,7 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
|
||||
* side-effects. The gpio can be manipulated later using standard GPIO API
|
||||
* calls.
|
||||
*/
|
||||
int nmk_config_pin(pin_cfg_t cfg)
|
||||
int nmk_config_pin(pin_cfg_t cfg, bool sleep)
|
||||
{
|
||||
struct nmk_gpio_chip *nmk_chip;
|
||||
int gpio = PIN_NUM(cfg);
|
||||
@ -186,7 +209,7 @@ int nmk_config_pin(pin_cfg_t cfg)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&nmk_chip->lock, flags);
|
||||
__nmk_config_pin(nmk_chip, gpio - nmk_chip->chip.base, cfg);
|
||||
__nmk_config_pin(nmk_chip, gpio - nmk_chip->chip.base, cfg, sleep);
|
||||
spin_unlock_irqrestore(&nmk_chip->lock, flags);
|
||||
|
||||
return 0;
|
||||
@ -207,7 +230,7 @@ int nmk_config_pins(pin_cfg_t *cfgs, int num)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
int ret = nmk_config_pin(cfgs[i]);
|
||||
ret = nmk_config_pin(cfgs[i], false);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
@ -216,6 +239,21 @@ int nmk_config_pins(pin_cfg_t *cfgs, int num)
|
||||
}
|
||||
EXPORT_SYMBOL(nmk_config_pins);
|
||||
|
||||
int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
ret = nmk_config_pin(cfgs[i], true);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nmk_config_pins_sleep);
|
||||
|
||||
/**
|
||||
* nmk_gpio_set_slpm() - configure the sleep mode of a pin
|
||||
* @gpio: pin number
|
||||
@ -634,7 +672,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
|
||||
|
||||
chip = &nmk_chip->chip;
|
||||
chip->base = pdata->first_gpio;
|
||||
chip->label = pdata->name;
|
||||
chip->label = pdata->name ?: dev_name(&dev->dev);
|
||||
chip->dev = &dev->dev;
|
||||
chip->owner = THIS_MODULE;
|
||||
|
||||
|
@ -19,16 +19,22 @@
|
||||
* bit 9..10 - Alternate Function Selection
|
||||
* bit 11..12 - Pull up/down state
|
||||
* bit 13 - Sleep mode behaviour
|
||||
* bit 14 - (sleep mode) Direction
|
||||
* bit 15 - (sleep mode) Value (if output)
|
||||
* bit 14 - Direction
|
||||
* bit 15 - Value (if output)
|
||||
* bit 16..18 - SLPM pull up/down state
|
||||
* bit 19..20 - SLPM direction
|
||||
* bit 21..22 - SLPM Value (if output)
|
||||
*
|
||||
* to facilitate the definition, the following macros are provided
|
||||
*
|
||||
* PIN_CFG_DEFAULT - default config (0):
|
||||
* pull up/down = disabled
|
||||
* sleep mode = input/wakeup
|
||||
* (sleep mode) direction = input
|
||||
* (sleep mode) value = low
|
||||
* direction = input
|
||||
* value = low
|
||||
* SLPM direction = same as normal
|
||||
* SLPM pull = same as normal
|
||||
* SLPM value = same as normal
|
||||
*
|
||||
* PIN_CFG - default config with alternate function
|
||||
* PIN_CFG_PULL - default config with alternate function and pull up/down
|
||||
@ -75,30 +81,64 @@ typedef unsigned long pin_cfg_t;
|
||||
#define PIN_VAL_LOW (0 << PIN_VAL_SHIFT)
|
||||
#define PIN_VAL_HIGH (1 << PIN_VAL_SHIFT)
|
||||
|
||||
/* Shortcuts. Use these instead of separate DIR and VAL. */
|
||||
#define PIN_INPUT PIN_DIR_INPUT
|
||||
#define PIN_SLPM_PULL_SHIFT 16
|
||||
#define PIN_SLPM_PULL_MASK (0x7 << PIN_SLPM_PULL_SHIFT)
|
||||
#define PIN_SLPM_PULL(x) \
|
||||
(((x) & PIN_SLPM_PULL_MASK) >> PIN_SLPM_PULL_SHIFT)
|
||||
#define PIN_SLPM_PULL_NONE \
|
||||
((1 + NMK_GPIO_PULL_NONE) << PIN_SLPM_PULL_SHIFT)
|
||||
#define PIN_SLPM_PULL_UP \
|
||||
((1 + NMK_GPIO_PULL_UP) << PIN_SLPM_PULL_SHIFT)
|
||||
#define PIN_SLPM_PULL_DOWN \
|
||||
((1 + NMK_GPIO_PULL_DOWN) << PIN_SLPM_PULL_SHIFT)
|
||||
|
||||
#define PIN_SLPM_DIR_SHIFT 19
|
||||
#define PIN_SLPM_DIR_MASK (0x3 << PIN_SLPM_DIR_SHIFT)
|
||||
#define PIN_SLPM_DIR(x) \
|
||||
(((x) & PIN_SLPM_DIR_MASK) >> PIN_SLPM_DIR_SHIFT)
|
||||
#define PIN_SLPM_DIR_INPUT ((1 + 0) << PIN_SLPM_DIR_SHIFT)
|
||||
#define PIN_SLPM_DIR_OUTPUT ((1 + 1) << PIN_SLPM_DIR_SHIFT)
|
||||
|
||||
#define PIN_SLPM_VAL_SHIFT 21
|
||||
#define PIN_SLPM_VAL_MASK (0x3 << PIN_SLPM_VAL_SHIFT)
|
||||
#define PIN_SLPM_VAL(x) \
|
||||
(((x) & PIN_SLPM_VAL_MASK) >> PIN_SLPM_VAL_SHIFT)
|
||||
#define PIN_SLPM_VAL_LOW ((1 + 0) << PIN_SLPM_VAL_SHIFT)
|
||||
#define PIN_SLPM_VAL_HIGH ((1 + 1) << PIN_SLPM_VAL_SHIFT)
|
||||
|
||||
/* Shortcuts. Use these instead of separate DIR, PULL, and VAL. */
|
||||
#define PIN_INPUT_PULLDOWN (PIN_DIR_INPUT | PIN_PULL_DOWN)
|
||||
#define PIN_INPUT_PULLUP (PIN_DIR_INPUT | PIN_PULL_UP)
|
||||
#define PIN_INPUT_NOPULL (PIN_DIR_INPUT | PIN_PULL_NONE)
|
||||
#define PIN_OUTPUT_LOW (PIN_DIR_OUTPUT | PIN_VAL_LOW)
|
||||
#define PIN_OUTPUT_HIGH (PIN_DIR_OUTPUT | PIN_VAL_HIGH)
|
||||
|
||||
/*
|
||||
* These are the same as the ones above, but should make more sense to the
|
||||
* reader when seen along with a setting a pin to AF mode.
|
||||
*/
|
||||
#define PIN_SLPM_INPUT PIN_INPUT
|
||||
#define PIN_SLPM_OUTPUT_LOW PIN_OUTPUT_LOW
|
||||
#define PIN_SLPM_OUTPUT_HIGH PIN_OUTPUT_HIGH
|
||||
#define PIN_SLPM_INPUT_PULLDOWN (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_DOWN)
|
||||
#define PIN_SLPM_INPUT_PULLUP (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_UP)
|
||||
#define PIN_SLPM_INPUT_NOPULL (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_NONE)
|
||||
#define PIN_SLPM_OUTPUT_LOW (PIN_SLPM_DIR_OUTPUT | PIN_SLPM_VAL_LOW)
|
||||
#define PIN_SLPM_OUTPUT_HIGH (PIN_SLPM_DIR_OUTPUT | PIN_SLPM_VAL_HIGH)
|
||||
|
||||
#define PIN_CFG_DEFAULT (PIN_PULL_NONE | PIN_SLPM_INPUT)
|
||||
#define PIN_CFG_DEFAULT (0)
|
||||
|
||||
#define PIN_CFG(num, alt) \
|
||||
(PIN_CFG_DEFAULT |\
|
||||
(PIN_NUM(num) | PIN_##alt))
|
||||
|
||||
#define PIN_CFG_INPUT(num, alt, pull) \
|
||||
(PIN_CFG_DEFAULT |\
|
||||
(PIN_NUM(num) | PIN_##alt | PIN_INPUT_##pull))
|
||||
|
||||
#define PIN_CFG_OUTPUT(num, alt, val) \
|
||||
(PIN_CFG_DEFAULT |\
|
||||
(PIN_NUM(num) | PIN_##alt | PIN_OUTPUT_##val))
|
||||
|
||||
#define PIN_CFG_PULL(num, alt, pull) \
|
||||
((PIN_CFG_DEFAULT & ~PIN_PULL_MASK) |\
|
||||
(PIN_NUM(num) | PIN_##alt | PIN_PULL_##pull))
|
||||
|
||||
extern int nmk_config_pin(pin_cfg_t cfg);
|
||||
extern int nmk_config_pin(pin_cfg_t cfg, bool sleep);
|
||||
extern int nmk_config_pins(pin_cfg_t *cfgs, int num);
|
||||
extern int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num);
|
||||
|
||||
#endif
|
||||
|
@ -230,11 +230,11 @@ config GPIO_STMPE
|
||||
This enables support for the GPIOs found on the STMPE I/O
|
||||
Expanders.
|
||||
|
||||
config GPIO_TC35892
|
||||
bool "TC35892 GPIOs"
|
||||
depends on MFD_TC35892
|
||||
config GPIO_TC3589X
|
||||
bool "TC3589X GPIOs"
|
||||
depends on MFD_TC3589X
|
||||
help
|
||||
This enables support for the GPIOs found on the TC35892
|
||||
This enables support for the GPIOs found on the TC3589X
|
||||
I/O Expander.
|
||||
|
||||
config GPIO_TWL4030
|
||||
|
@ -24,7 +24,7 @@ obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
|
||||
obj-$(CONFIG_GPIO_PCH) += pch_gpio.o
|
||||
obj-$(CONFIG_GPIO_PL061) += pl061.o
|
||||
obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o
|
||||
obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o
|
||||
obj-$(CONFIG_GPIO_TC3589X) += tc3589x-gpio.o
|
||||
obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
|
||||
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
|
||||
obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o
|
||||
|
@ -1,389 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License, version 2
|
||||
* Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/tc35892.h>
|
||||
|
||||
/*
|
||||
* These registers are modified under the irq bus lock and cached to avoid
|
||||
* unnecessary writes in bus_sync_unlock.
|
||||
*/
|
||||
enum { REG_IBE, REG_IEV, REG_IS, REG_IE };
|
||||
|
||||
#define CACHE_NR_REGS 4
|
||||
#define CACHE_NR_BANKS 3
|
||||
|
||||
struct tc35892_gpio {
|
||||
struct gpio_chip chip;
|
||||
struct tc35892 *tc35892;
|
||||
struct device *dev;
|
||||
struct mutex irq_lock;
|
||||
|
||||
int irq_base;
|
||||
|
||||
/* Caches of interrupt control registers for bus_lock */
|
||||
u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
|
||||
u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
|
||||
};
|
||||
|
||||
static inline struct tc35892_gpio *to_tc35892_gpio(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct tc35892_gpio, chip);
|
||||
}
|
||||
|
||||
static int tc35892_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2;
|
||||
u8 mask = 1 << (offset % 8);
|
||||
int ret;
|
||||
|
||||
ret = tc35892_reg_read(tc35892, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret & mask;
|
||||
}
|
||||
|
||||
static void tc35892_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2;
|
||||
unsigned pos = offset % 8;
|
||||
u8 data[] = {!!val << pos, 1 << pos};
|
||||
|
||||
tc35892_block_write(tc35892, reg, ARRAY_SIZE(data), data);
|
||||
}
|
||||
|
||||
static int tc35892_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int val)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
u8 reg = TC35892_GPIODIR0 + offset / 8;
|
||||
unsigned pos = offset % 8;
|
||||
|
||||
tc35892_gpio_set(chip, offset, val);
|
||||
|
||||
return tc35892_set_bits(tc35892, reg, 1 << pos, 1 << pos);
|
||||
}
|
||||
|
||||
static int tc35892_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned offset)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
u8 reg = TC35892_GPIODIR0 + offset / 8;
|
||||
unsigned pos = offset % 8;
|
||||
|
||||
return tc35892_set_bits(tc35892, reg, 1 << pos, 0);
|
||||
}
|
||||
|
||||
static int tc35892_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
|
||||
|
||||
return tc35892_gpio->irq_base + offset;
|
||||
}
|
||||
|
||||
static struct gpio_chip template_chip = {
|
||||
.label = "tc35892",
|
||||
.owner = THIS_MODULE,
|
||||
.direction_input = tc35892_gpio_direction_input,
|
||||
.get = tc35892_gpio_get,
|
||||
.direction_output = tc35892_gpio_direction_output,
|
||||
.set = tc35892_gpio_set,
|
||||
.to_irq = tc35892_gpio_to_irq,
|
||||
.can_sleep = 1,
|
||||
};
|
||||
|
||||
static int tc35892_gpio_irq_set_type(unsigned int irq, unsigned int type)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tc35892_gpio->irq_base;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
if (type == IRQ_TYPE_EDGE_BOTH) {
|
||||
tc35892_gpio->regs[REG_IBE][regoffset] |= mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tc35892_gpio->regs[REG_IBE][regoffset] &= ~mask;
|
||||
|
||||
if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
|
||||
tc35892_gpio->regs[REG_IS][regoffset] |= mask;
|
||||
else
|
||||
tc35892_gpio->regs[REG_IS][regoffset] &= ~mask;
|
||||
|
||||
if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
|
||||
tc35892_gpio->regs[REG_IEV][regoffset] |= mask;
|
||||
else
|
||||
tc35892_gpio->regs[REG_IEV][regoffset] &= ~mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc35892_gpio_irq_lock(unsigned int irq)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
|
||||
|
||||
mutex_lock(&tc35892_gpio->irq_lock);
|
||||
}
|
||||
|
||||
static void tc35892_gpio_irq_sync_unlock(unsigned int irq)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
static const u8 regmap[] = {
|
||||
[REG_IBE] = TC35892_GPIOIBE0,
|
||||
[REG_IEV] = TC35892_GPIOIEV0,
|
||||
[REG_IS] = TC35892_GPIOIS0,
|
||||
[REG_IE] = TC35892_GPIOIE0,
|
||||
};
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < CACHE_NR_REGS; i++) {
|
||||
for (j = 0; j < CACHE_NR_BANKS; j++) {
|
||||
u8 old = tc35892_gpio->oldregs[i][j];
|
||||
u8 new = tc35892_gpio->regs[i][j];
|
||||
|
||||
if (new == old)
|
||||
continue;
|
||||
|
||||
tc35892_gpio->oldregs[i][j] = new;
|
||||
tc35892_reg_write(tc35892, regmap[i] + j * 8, new);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&tc35892_gpio->irq_lock);
|
||||
}
|
||||
|
||||
static void tc35892_gpio_irq_mask(unsigned int irq)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tc35892_gpio->irq_base;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
tc35892_gpio->regs[REG_IE][regoffset] &= ~mask;
|
||||
}
|
||||
|
||||
static void tc35892_gpio_irq_unmask(unsigned int irq)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tc35892_gpio->irq_base;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
tc35892_gpio->regs[REG_IE][regoffset] |= mask;
|
||||
}
|
||||
|
||||
static struct irq_chip tc35892_gpio_irq_chip = {
|
||||
.name = "tc35892-gpio",
|
||||
.bus_lock = tc35892_gpio_irq_lock,
|
||||
.bus_sync_unlock = tc35892_gpio_irq_sync_unlock,
|
||||
.mask = tc35892_gpio_irq_mask,
|
||||
.unmask = tc35892_gpio_irq_unmask,
|
||||
.set_type = tc35892_gpio_irq_set_type,
|
||||
};
|
||||
|
||||
static irqreturn_t tc35892_gpio_irq(int irq, void *dev)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = dev;
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
u8 status[CACHE_NR_BANKS];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = tc35892_block_read(tc35892, TC35892_GPIOMIS0,
|
||||
ARRAY_SIZE(status), status);
|
||||
if (ret < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(status); i++) {
|
||||
unsigned int stat = status[i];
|
||||
if (!stat)
|
||||
continue;
|
||||
|
||||
while (stat) {
|
||||
int bit = __ffs(stat);
|
||||
int line = i * 8 + bit;
|
||||
|
||||
handle_nested_irq(tc35892_gpio->irq_base + line);
|
||||
stat &= ~(1 << bit);
|
||||
}
|
||||
|
||||
tc35892_reg_write(tc35892, TC35892_GPIOIC0 + i, status[i]);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tc35892_gpio_irq_init(struct tc35892_gpio *tc35892_gpio)
|
||||
{
|
||||
int base = tc35892_gpio->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) {
|
||||
set_irq_chip_data(irq, tc35892_gpio);
|
||||
set_irq_chip_and_handler(irq, &tc35892_gpio_irq_chip,
|
||||
handle_simple_irq);
|
||||
set_irq_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
set_irq_noprobe(irq);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc35892_gpio_irq_remove(struct tc35892_gpio *tc35892_gpio)
|
||||
{
|
||||
int base = tc35892_gpio->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) {
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, 0);
|
||||
#endif
|
||||
set_irq_chip_and_handler(irq, NULL, NULL);
|
||||
set_irq_chip_data(irq, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit tc35892_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tc35892 *tc35892 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct tc35892_gpio_platform_data *pdata;
|
||||
struct tc35892_gpio *tc35892_gpio;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
pdata = tc35892->pdata->gpio;
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
tc35892_gpio = kzalloc(sizeof(struct tc35892_gpio), GFP_KERNEL);
|
||||
if (!tc35892_gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&tc35892_gpio->irq_lock);
|
||||
|
||||
tc35892_gpio->dev = &pdev->dev;
|
||||
tc35892_gpio->tc35892 = tc35892;
|
||||
|
||||
tc35892_gpio->chip = template_chip;
|
||||
tc35892_gpio->chip.ngpio = tc35892->num_gpio;
|
||||
tc35892_gpio->chip.dev = &pdev->dev;
|
||||
tc35892_gpio->chip.base = pdata->gpio_base;
|
||||
|
||||
tc35892_gpio->irq_base = tc35892->irq_base + TC35892_INT_GPIO(0);
|
||||
|
||||
/* Bring the GPIO module out of reset */
|
||||
ret = tc35892_set_bits(tc35892, TC35892_RSTCTRL,
|
||||
TC35892_RSTCTRL_GPIRST, 0);
|
||||
if (ret < 0)
|
||||
goto out_free;
|
||||
|
||||
ret = tc35892_gpio_irq_init(tc35892_gpio);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, tc35892_gpio_irq, IRQF_ONESHOT,
|
||||
"tc35892-gpio", tc35892_gpio);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
|
||||
goto out_removeirq;
|
||||
}
|
||||
|
||||
ret = gpiochip_add(&tc35892_gpio->chip);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
|
||||
goto out_freeirq;
|
||||
}
|
||||
|
||||
if (pdata->setup)
|
||||
pdata->setup(tc35892, tc35892_gpio->chip.base);
|
||||
|
||||
platform_set_drvdata(pdev, tc35892_gpio);
|
||||
|
||||
return 0;
|
||||
|
||||
out_freeirq:
|
||||
free_irq(irq, tc35892_gpio);
|
||||
out_removeirq:
|
||||
tc35892_gpio_irq_remove(tc35892_gpio);
|
||||
out_free:
|
||||
kfree(tc35892_gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit tc35892_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = platform_get_drvdata(pdev);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
struct tc35892_gpio_platform_data *pdata = tc35892->pdata->gpio;
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
int ret;
|
||||
|
||||
if (pdata->remove)
|
||||
pdata->remove(tc35892, tc35892_gpio->chip.base);
|
||||
|
||||
ret = gpiochip_remove(&tc35892_gpio->chip);
|
||||
if (ret < 0) {
|
||||
dev_err(tc35892_gpio->dev,
|
||||
"unable to remove gpiochip: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
free_irq(irq, tc35892_gpio);
|
||||
tc35892_gpio_irq_remove(tc35892_gpio);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(tc35892_gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tc35892_gpio_driver = {
|
||||
.driver.name = "tc35892-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = tc35892_gpio_probe,
|
||||
.remove = __devexit_p(tc35892_gpio_remove),
|
||||
};
|
||||
|
||||
static int __init tc35892_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&tc35892_gpio_driver);
|
||||
}
|
||||
subsys_initcall(tc35892_gpio_init);
|
||||
|
||||
static void __exit tc35892_gpio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tc35892_gpio_driver);
|
||||
}
|
||||
module_exit(tc35892_gpio_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("TC35892 GPIO driver");
|
||||
MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
|
389
drivers/gpio/tc3589x-gpio.c
Normal file
389
drivers/gpio/tc3589x-gpio.c
Normal file
@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License, version 2
|
||||
* Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/tc3589x.h>
|
||||
|
||||
/*
|
||||
* These registers are modified under the irq bus lock and cached to avoid
|
||||
* unnecessary writes in bus_sync_unlock.
|
||||
*/
|
||||
enum { REG_IBE, REG_IEV, REG_IS, REG_IE };
|
||||
|
||||
#define CACHE_NR_REGS 4
|
||||
#define CACHE_NR_BANKS 3
|
||||
|
||||
struct tc3589x_gpio {
|
||||
struct gpio_chip chip;
|
||||
struct tc3589x *tc3589x;
|
||||
struct device *dev;
|
||||
struct mutex irq_lock;
|
||||
|
||||
int irq_base;
|
||||
|
||||
/* Caches of interrupt control registers for bus_lock */
|
||||
u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
|
||||
u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
|
||||
};
|
||||
|
||||
static inline struct tc3589x_gpio *to_tc3589x_gpio(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct tc3589x_gpio, chip);
|
||||
}
|
||||
|
||||
static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
|
||||
struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
|
||||
u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2;
|
||||
u8 mask = 1 << (offset % 8);
|
||||
int ret;
|
||||
|
||||
ret = tc3589x_reg_read(tc3589x, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret & mask;
|
||||
}
|
||||
|
||||
static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
|
||||
struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
|
||||
u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2;
|
||||
unsigned pos = offset % 8;
|
||||
u8 data[] = {!!val << pos, 1 << pos};
|
||||
|
||||
tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data);
|
||||
}
|
||||
|
||||
static int tc3589x_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int val)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
|
||||
struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
|
||||
u8 reg = TC3589x_GPIODIR0 + offset / 8;
|
||||
unsigned pos = offset % 8;
|
||||
|
||||
tc3589x_gpio_set(chip, offset, val);
|
||||
|
||||
return tc3589x_set_bits(tc3589x, reg, 1 << pos, 1 << pos);
|
||||
}
|
||||
|
||||
static int tc3589x_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned offset)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
|
||||
struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
|
||||
u8 reg = TC3589x_GPIODIR0 + offset / 8;
|
||||
unsigned pos = offset % 8;
|
||||
|
||||
return tc3589x_set_bits(tc3589x, reg, 1 << pos, 0);
|
||||
}
|
||||
|
||||
static int tc3589x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
|
||||
|
||||
return tc3589x_gpio->irq_base + offset;
|
||||
}
|
||||
|
||||
static struct gpio_chip template_chip = {
|
||||
.label = "tc3589x",
|
||||
.owner = THIS_MODULE,
|
||||
.direction_input = tc3589x_gpio_direction_input,
|
||||
.get = tc3589x_gpio_get,
|
||||
.direction_output = tc3589x_gpio_direction_output,
|
||||
.set = tc3589x_gpio_set,
|
||||
.to_irq = tc3589x_gpio_to_irq,
|
||||
.can_sleep = 1,
|
||||
};
|
||||
|
||||
static int tc3589x_gpio_irq_set_type(unsigned int irq, unsigned int type)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tc3589x_gpio->irq_base;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
if (type == IRQ_TYPE_EDGE_BOTH) {
|
||||
tc3589x_gpio->regs[REG_IBE][regoffset] |= mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tc3589x_gpio->regs[REG_IBE][regoffset] &= ~mask;
|
||||
|
||||
if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
|
||||
tc3589x_gpio->regs[REG_IS][regoffset] |= mask;
|
||||
else
|
||||
tc3589x_gpio->regs[REG_IS][regoffset] &= ~mask;
|
||||
|
||||
if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
|
||||
tc3589x_gpio->regs[REG_IEV][regoffset] |= mask;
|
||||
else
|
||||
tc3589x_gpio->regs[REG_IEV][regoffset] &= ~mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc3589x_gpio_irq_lock(unsigned int irq)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq);
|
||||
|
||||
mutex_lock(&tc3589x_gpio->irq_lock);
|
||||
}
|
||||
|
||||
static void tc3589x_gpio_irq_sync_unlock(unsigned int irq)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq);
|
||||
struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
|
||||
static const u8 regmap[] = {
|
||||
[REG_IBE] = TC3589x_GPIOIBE0,
|
||||
[REG_IEV] = TC3589x_GPIOIEV0,
|
||||
[REG_IS] = TC3589x_GPIOIS0,
|
||||
[REG_IE] = TC3589x_GPIOIE0,
|
||||
};
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < CACHE_NR_REGS; i++) {
|
||||
for (j = 0; j < CACHE_NR_BANKS; j++) {
|
||||
u8 old = tc3589x_gpio->oldregs[i][j];
|
||||
u8 new = tc3589x_gpio->regs[i][j];
|
||||
|
||||
if (new == old)
|
||||
continue;
|
||||
|
||||
tc3589x_gpio->oldregs[i][j] = new;
|
||||
tc3589x_reg_write(tc3589x, regmap[i] + j * 8, new);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&tc3589x_gpio->irq_lock);
|
||||
}
|
||||
|
||||
static void tc3589x_gpio_irq_mask(unsigned int irq)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tc3589x_gpio->irq_base;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask;
|
||||
}
|
||||
|
||||
static void tc3589x_gpio_irq_unmask(unsigned int irq)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tc3589x_gpio->irq_base;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
tc3589x_gpio->regs[REG_IE][regoffset] |= mask;
|
||||
}
|
||||
|
||||
static struct irq_chip tc3589x_gpio_irq_chip = {
|
||||
.name = "tc3589x-gpio",
|
||||
.bus_lock = tc3589x_gpio_irq_lock,
|
||||
.bus_sync_unlock = tc3589x_gpio_irq_sync_unlock,
|
||||
.mask = tc3589x_gpio_irq_mask,
|
||||
.unmask = tc3589x_gpio_irq_unmask,
|
||||
.set_type = tc3589x_gpio_irq_set_type,
|
||||
};
|
||||
|
||||
static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = dev;
|
||||
struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
|
||||
u8 status[CACHE_NR_BANKS];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = tc3589x_block_read(tc3589x, TC3589x_GPIOMIS0,
|
||||
ARRAY_SIZE(status), status);
|
||||
if (ret < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(status); i++) {
|
||||
unsigned int stat = status[i];
|
||||
if (!stat)
|
||||
continue;
|
||||
|
||||
while (stat) {
|
||||
int bit = __ffs(stat);
|
||||
int line = i * 8 + bit;
|
||||
|
||||
handle_nested_irq(tc3589x_gpio->irq_base + line);
|
||||
stat &= ~(1 << bit);
|
||||
}
|
||||
|
||||
tc3589x_reg_write(tc3589x, TC3589x_GPIOIC0 + i, status[i]);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio)
|
||||
{
|
||||
int base = tc3589x_gpio->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) {
|
||||
set_irq_chip_data(irq, tc3589x_gpio);
|
||||
set_irq_chip_and_handler(irq, &tc3589x_gpio_irq_chip,
|
||||
handle_simple_irq);
|
||||
set_irq_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
set_irq_noprobe(irq);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc3589x_gpio_irq_remove(struct tc3589x_gpio *tc3589x_gpio)
|
||||
{
|
||||
int base = tc3589x_gpio->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) {
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, 0);
|
||||
#endif
|
||||
set_irq_chip_and_handler(irq, NULL, NULL);
|
||||
set_irq_chip_data(irq, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct tc3589x_gpio_platform_data *pdata;
|
||||
struct tc3589x_gpio *tc3589x_gpio;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
pdata = tc3589x->pdata->gpio;
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
tc3589x_gpio = kzalloc(sizeof(struct tc3589x_gpio), GFP_KERNEL);
|
||||
if (!tc3589x_gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&tc3589x_gpio->irq_lock);
|
||||
|
||||
tc3589x_gpio->dev = &pdev->dev;
|
||||
tc3589x_gpio->tc3589x = tc3589x;
|
||||
|
||||
tc3589x_gpio->chip = template_chip;
|
||||
tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;
|
||||
tc3589x_gpio->chip.dev = &pdev->dev;
|
||||
tc3589x_gpio->chip.base = pdata->gpio_base;
|
||||
|
||||
tc3589x_gpio->irq_base = tc3589x->irq_base + TC3589x_INT_GPIO(0);
|
||||
|
||||
/* Bring the GPIO module out of reset */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL,
|
||||
TC3589x_RSTCTRL_GPIRST, 0);
|
||||
if (ret < 0)
|
||||
goto out_free;
|
||||
|
||||
ret = tc3589x_gpio_irq_init(tc3589x_gpio);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, tc3589x_gpio_irq, IRQF_ONESHOT,
|
||||
"tc3589x-gpio", tc3589x_gpio);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
|
||||
goto out_removeirq;
|
||||
}
|
||||
|
||||
ret = gpiochip_add(&tc3589x_gpio->chip);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
|
||||
goto out_freeirq;
|
||||
}
|
||||
|
||||
if (pdata->setup)
|
||||
pdata->setup(tc3589x, tc3589x_gpio->chip.base);
|
||||
|
||||
platform_set_drvdata(pdev, tc3589x_gpio);
|
||||
|
||||
return 0;
|
||||
|
||||
out_freeirq:
|
||||
free_irq(irq, tc3589x_gpio);
|
||||
out_removeirq:
|
||||
tc3589x_gpio_irq_remove(tc3589x_gpio);
|
||||
out_free:
|
||||
kfree(tc3589x_gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit tc3589x_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tc3589x_gpio *tc3589x_gpio = platform_get_drvdata(pdev);
|
||||
struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
|
||||
struct tc3589x_gpio_platform_data *pdata = tc3589x->pdata->gpio;
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
int ret;
|
||||
|
||||
if (pdata->remove)
|
||||
pdata->remove(tc3589x, tc3589x_gpio->chip.base);
|
||||
|
||||
ret = gpiochip_remove(&tc3589x_gpio->chip);
|
||||
if (ret < 0) {
|
||||
dev_err(tc3589x_gpio->dev,
|
||||
"unable to remove gpiochip: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
free_irq(irq, tc3589x_gpio);
|
||||
tc3589x_gpio_irq_remove(tc3589x_gpio);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(tc3589x_gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tc3589x_gpio_driver = {
|
||||
.driver.name = "tc3589x-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = tc3589x_gpio_probe,
|
||||
.remove = __devexit_p(tc3589x_gpio_remove),
|
||||
};
|
||||
|
||||
static int __init tc3589x_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&tc3589x_gpio_driver);
|
||||
}
|
||||
subsys_initcall(tc3589x_gpio_init);
|
||||
|
||||
static void __exit tc3589x_gpio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tc3589x_gpio_driver);
|
||||
}
|
||||
module_exit(tc3589x_gpio_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("TC3589x GPIO driver");
|
||||
MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
|
@ -459,6 +459,16 @@ config KEYBOARD_OMAP4
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called omap4-keypad.
|
||||
|
||||
config KEYBOARD_TC3589X
|
||||
tristate "TC3589X Keypad support"
|
||||
depends on MFD_TC3589X
|
||||
help
|
||||
Say Y here if you want to use the keypad controller on
|
||||
TC35892/3 I/O expander.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called tc3589x-keypad.
|
||||
|
||||
config KEYBOARD_TNETV107X
|
||||
tristate "TI TNETV107X keypad support"
|
||||
depends on ARCH_DAVINCI_TNETV107X
|
||||
|
@ -41,6 +41,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
|
||||
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
|
||||
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
|
||||
|
472
drivers/input/keyboard/tc3589x-keypad.c
Normal file
472
drivers/input/keyboard/tc3589x-keypad.c
Normal file
@ -0,0 +1,472 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Jayeeta Banerjee <jayeeta.banerjee@stericsson.com>
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com>
|
||||
*
|
||||
* License Terms: GNU General Public License, version 2
|
||||
*
|
||||
* TC35893 MFD Keypad Controller driver
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/tc3589x.h>
|
||||
|
||||
/* Maximum supported keypad matrix row/columns size */
|
||||
#define TC3589x_MAX_KPROW 8
|
||||
#define TC3589x_MAX_KPCOL 12
|
||||
|
||||
/* keypad related Constants */
|
||||
#define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF
|
||||
#define DEDICATED_KEY_VAL 0xFF
|
||||
|
||||
/* Pull up/down masks */
|
||||
#define TC3589x_NO_PULL_MASK 0x0
|
||||
#define TC3589x_PULL_DOWN_MASK 0x1
|
||||
#define TC3589x_PULL_UP_MASK 0x2
|
||||
#define TC3589x_PULLUP_ALL_MASK 0xAA
|
||||
#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2))
|
||||
|
||||
/* Bit masks for IOCFG register */
|
||||
#define IOCFG_BALLCFG 0x01
|
||||
#define IOCFG_IG 0x08
|
||||
|
||||
#define KP_EVCODE_COL_MASK 0x0F
|
||||
#define KP_EVCODE_ROW_MASK 0x70
|
||||
#define KP_RELEASE_EVT_MASK 0x80
|
||||
|
||||
#define KP_ROW_SHIFT 4
|
||||
|
||||
#define KP_NO_VALID_KEY_MASK 0x7F
|
||||
|
||||
/* bit masks for RESTCTRL register */
|
||||
#define TC3589x_KBDRST 0x2
|
||||
#define TC3589x_IRQRST 0x10
|
||||
#define TC3589x_RESET_ALL 0x1B
|
||||
|
||||
/* KBDMFS register bit mask */
|
||||
#define TC3589x_KBDMFS_EN 0x1
|
||||
|
||||
/* CLKEN register bitmask */
|
||||
#define KPD_CLK_EN 0x1
|
||||
|
||||
/* RSTINTCLR register bit mask */
|
||||
#define IRQ_CLEAR 0x1
|
||||
|
||||
/* bit masks for keyboard interrupts*/
|
||||
#define TC3589x_EVT_LOSS_INT 0x8
|
||||
#define TC3589x_EVT_INT 0x4
|
||||
#define TC3589x_KBD_LOSS_INT 0x2
|
||||
#define TC3589x_KBD_INT 0x1
|
||||
|
||||
/* bit masks for keyboard interrupt clear*/
|
||||
#define TC3589x_EVT_INT_CLR 0x2
|
||||
#define TC3589x_KBD_INT_CLR 0x1
|
||||
|
||||
#define TC3589x_KBD_KEYMAP_SIZE 64
|
||||
|
||||
/**
|
||||
* struct tc_keypad - data structure used by keypad driver
|
||||
* @input: pointer to input device object
|
||||
* @board: keypad platform device
|
||||
* @krow: number of rows
|
||||
* @kcol: number of coloumns
|
||||
* @keymap: matrix scan code table for keycodes
|
||||
*/
|
||||
struct tc_keypad {
|
||||
struct tc3589x *tc3589x;
|
||||
struct input_dev *input;
|
||||
const struct tc3589x_keypad_platform_data *board;
|
||||
unsigned int krow;
|
||||
unsigned int kcol;
|
||||
unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE];
|
||||
bool keypad_stopped;
|
||||
};
|
||||
|
||||
static int __devinit tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad)
|
||||
{
|
||||
int ret;
|
||||
struct tc3589x *tc3589x = keypad->tc3589x;
|
||||
u8 settle_time = keypad->board->settle_time;
|
||||
u8 dbounce_period = keypad->board->debounce_period;
|
||||
u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */
|
||||
u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */
|
||||
|
||||
/* validate platform configurations */
|
||||
if (keypad->board->kcol > TC3589x_MAX_KPCOL ||
|
||||
keypad->board->krow > TC3589x_MAX_KPROW ||
|
||||
keypad->board->debounce_period > TC3589x_MAX_DEBOUNCE_SETTLE ||
|
||||
keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE)
|
||||
return -EINVAL;
|
||||
|
||||
/* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE,
|
||||
(rows << KP_ROW_SHIFT) | column);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* configure dedicated key config, no dedicated key selected */
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Configure settle time */
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG, settle_time);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Configure debounce time */
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE, dbounce_period);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Start of initialise keypad GPIOs */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Configure pull-up resistors for all row GPIOs */
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB,
|
||||
TC3589x_PULLUP_ALL_MASK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB,
|
||||
TC3589x_PULLUP_ALL_MASK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Configure pull-up resistors for all column GPIOs */
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB,
|
||||
TC3589x_PULLUP_ALL_MASK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB,
|
||||
TC3589x_PULLUP_ALL_MASK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB,
|
||||
TC3589x_PULLUP_ALL_MASK);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define TC35893_DATA_REGS 4
|
||||
#define TC35893_KEYCODE_FIFO_EMPTY 0x7f
|
||||
#define TC35893_KEYCODE_FIFO_CLEAR 0xff
|
||||
#define TC35893_KEYPAD_ROW_SHIFT 0x3
|
||||
|
||||
static irqreturn_t tc3589x_keypad_irq(int irq, void *dev)
|
||||
{
|
||||
struct tc_keypad *keypad = dev;
|
||||
struct tc3589x *tc3589x = keypad->tc3589x;
|
||||
u8 i, row_index, col_index, kbd_code, up;
|
||||
u8 code;
|
||||
|
||||
for (i = 0; i < TC35893_DATA_REGS * 2; i++) {
|
||||
kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO);
|
||||
|
||||
/* loop till fifo is empty and no more keys are pressed */
|
||||
if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY ||
|
||||
kbd_code == TC35893_KEYCODE_FIFO_CLEAR)
|
||||
continue;
|
||||
|
||||
/* valid key is found */
|
||||
col_index = kbd_code & KP_EVCODE_COL_MASK;
|
||||
row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT;
|
||||
code = MATRIX_SCAN_CODE(row_index, col_index,
|
||||
TC35893_KEYPAD_ROW_SHIFT);
|
||||
up = kbd_code & KP_RELEASE_EVT_MASK;
|
||||
|
||||
input_event(keypad->input, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(keypad->input, keypad->keymap[code], !up);
|
||||
input_sync(keypad->input);
|
||||
}
|
||||
|
||||
/* clear IRQ */
|
||||
tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
|
||||
0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
|
||||
/* enable IRQ */
|
||||
tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
|
||||
0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tc3589x_keypad_enable(struct tc_keypad *keypad)
|
||||
{
|
||||
struct tc3589x *tc3589x = keypad->tc3589x;
|
||||
int ret;
|
||||
|
||||
/* pull the keypad module out of reset */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* configure KBDMFS */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, TC3589x_KBDMFS_EN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* enable the keypad clock */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* clear pending IRQs */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* enable the IRQs */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0,
|
||||
TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
keypad->keypad_stopped = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tc3589x_keypad_disable(struct tc_keypad *keypad)
|
||||
{
|
||||
struct tc3589x *tc3589x = keypad->tc3589x;
|
||||
int ret;
|
||||
|
||||
/* clear IRQ */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
|
||||
0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* disable all interrupts */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
|
||||
~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* disable the keypad module */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* put the keypad module into reset */
|
||||
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x1);
|
||||
|
||||
keypad->keypad_stopped = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tc3589x_keypad_open(struct input_dev *input)
|
||||
{
|
||||
int error;
|
||||
struct tc_keypad *keypad = input_get_drvdata(input);
|
||||
|
||||
/* enable the keypad module */
|
||||
error = tc3589x_keypad_enable(keypad);
|
||||
if (error < 0) {
|
||||
dev_err(&input->dev, "failed to enable keypad module\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = tc3589x_keypad_init_key_hardware(keypad);
|
||||
if (error < 0) {
|
||||
dev_err(&input->dev, "failed to configure keypad module\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc3589x_keypad_close(struct input_dev *input)
|
||||
{
|
||||
struct tc_keypad *keypad = input_get_drvdata(input);
|
||||
|
||||
/* disable the keypad module */
|
||||
tc3589x_keypad_disable(keypad);
|
||||
}
|
||||
|
||||
static int __devinit tc3589x_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct tc_keypad *keypad;
|
||||
struct input_dev *input;
|
||||
const struct tc3589x_keypad_platform_data *plat;
|
||||
int error, irq;
|
||||
|
||||
plat = tc3589x->pdata->keypad;
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev, "invalid keypad platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!keypad || !input) {
|
||||
dev_err(&pdev->dev, "failed to allocate keypad memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
keypad->board = plat;
|
||||
keypad->input = input;
|
||||
keypad->tc3589x = tc3589x;
|
||||
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->name = pdev->name;
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
input->keycode = keypad->keymap;
|
||||
input->keycodesize = sizeof(keypad->keymap[0]);
|
||||
input->keycodemax = ARRAY_SIZE(keypad->keymap);
|
||||
|
||||
input->open = tc3589x_keypad_open;
|
||||
input->close = tc3589x_keypad_close;
|
||||
|
||||
input_set_drvdata(input, keypad);
|
||||
|
||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
if (!plat->no_autorepeat)
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
matrix_keypad_build_keymap(plat->keymap_data, 0x3,
|
||||
input->keycode, input->keybit);
|
||||
|
||||
error = request_threaded_irq(irq, NULL,
|
||||
tc3589x_keypad_irq, plat->irqtype,
|
||||
"tc3589x-keypad", keypad);
|
||||
if (error < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Could not allocate irq %d,error %d\n",
|
||||
irq, error);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Could not register input device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
/* let platform decide if keypad is a wakeup source or not */
|
||||
device_init_wakeup(&pdev->dev, plat->enable_wakeup);
|
||||
device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup);
|
||||
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(irq, keypad);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(keypad);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit tc3589x_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tc_keypad *keypad = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (!keypad->keypad_stopped)
|
||||
tc3589x_keypad_disable(keypad);
|
||||
|
||||
free_irq(irq, keypad);
|
||||
|
||||
input_unregister_device(keypad->input);
|
||||
|
||||
kfree(keypad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tc3589x_keypad_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct tc_keypad *keypad = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
/* keypad is already off; we do nothing */
|
||||
if (keypad->keypad_stopped)
|
||||
return 0;
|
||||
|
||||
/* if device is not a wakeup source, disable it for powersave */
|
||||
if (!device_may_wakeup(&pdev->dev))
|
||||
tc3589x_keypad_disable(keypad);
|
||||
else
|
||||
enable_irq_wake(irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc3589x_keypad_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct tc_keypad *keypad = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (!keypad->keypad_stopped)
|
||||
return 0;
|
||||
|
||||
/* enable the device to resume normal operations */
|
||||
if (!device_may_wakeup(&pdev->dev))
|
||||
tc3589x_keypad_enable(keypad);
|
||||
else
|
||||
disable_irq_wake(irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops,
|
||||
tc3589x_keypad_suspend, tc3589x_keypad_resume);
|
||||
#endif
|
||||
|
||||
static struct platform_driver tc3589x_keypad_driver = {
|
||||
.driver.name = "tc3589x-keypad",
|
||||
.driver.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.driver.pm = &tc3589x_keypad_dev_pm_ops,
|
||||
#endif
|
||||
.probe = tc3589x_keypad_probe,
|
||||
.remove = __devexit_p(tc3589x_keypad_remove),
|
||||
};
|
||||
|
||||
static int __init tc3589x_keypad_init(void)
|
||||
{
|
||||
return platform_driver_register(&tc3589x_keypad_driver);
|
||||
}
|
||||
module_init(tc3589x_keypad_init);
|
||||
|
||||
static void __exit tc3589x_keypad_exit(void)
|
||||
{
|
||||
return platform_driver_unregister(&tc3589x_keypad_driver);
|
||||
}
|
||||
module_exit(tc3589x_keypad_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer");
|
||||
MODULE_DESCRIPTION("TC35893 Keypad Driver");
|
||||
MODULE_ALIAS("platform:tc3589x-keypad")
|
@ -218,12 +218,12 @@ config MFD_STMPE
|
||||
Keypad: stmpe-keypad
|
||||
Touchscreen: stmpe-ts
|
||||
|
||||
config MFD_TC35892
|
||||
bool "Support Toshiba TC35892"
|
||||
config MFD_TC3589X
|
||||
bool "Support Toshiba TC35892 and variants"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
select MFD_CORE
|
||||
help
|
||||
Support for the Toshiba TC35892 I/O Expander.
|
||||
Support for the Toshiba TC35892 and variants I/O Expander.
|
||||
|
||||
This driver provides common support for accessing the device,
|
||||
additional drivers must be enabled in order to use the
|
||||
|
@ -16,7 +16,7 @@ obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
|
||||
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
|
||||
|
||||
obj-$(CONFIG_MFD_STMPE) += stmpe.o
|
||||
obj-$(CONFIG_MFD_TC35892) += tc35892.o
|
||||
obj-$(CONFIG_MFD_TC3589X) += tc3589x.o
|
||||
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
|
||||
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
|
||||
obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
|
||||
|
@ -1,345 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License, version 2
|
||||
* Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/tc35892.h>
|
||||
|
||||
/**
|
||||
* tc35892_reg_read() - read a single TC35892 register
|
||||
* @tc35892: Device to read from
|
||||
* @reg: Register to read
|
||||
*/
|
||||
int tc35892_reg_read(struct tc35892 *tc35892, u8 reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(tc35892->i2c, reg);
|
||||
if (ret < 0)
|
||||
dev_err(tc35892->dev, "failed to read reg %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc35892_reg_read);
|
||||
|
||||
/**
|
||||
* tc35892_reg_read() - write a single TC35892 register
|
||||
* @tc35892: Device to write to
|
||||
* @reg: Register to read
|
||||
* @data: Value to write
|
||||
*/
|
||||
int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(tc35892->i2c, reg, data);
|
||||
if (ret < 0)
|
||||
dev_err(tc35892->dev, "failed to write reg %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc35892_reg_write);
|
||||
|
||||
/**
|
||||
* tc35892_block_read() - read multiple TC35892 registers
|
||||
* @tc35892: Device to read from
|
||||
* @reg: First register
|
||||
* @length: Number of registers
|
||||
* @values: Buffer to write to
|
||||
*/
|
||||
int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, u8 *values)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(tc35892->i2c, reg, length, values);
|
||||
if (ret < 0)
|
||||
dev_err(tc35892->dev, "failed to read regs %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc35892_block_read);
|
||||
|
||||
/**
|
||||
* tc35892_block_write() - write multiple TC35892 registers
|
||||
* @tc35892: Device to write to
|
||||
* @reg: First register
|
||||
* @length: Number of registers
|
||||
* @values: Values to write
|
||||
*/
|
||||
int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length,
|
||||
const u8 *values)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(tc35892->i2c, reg, length,
|
||||
values);
|
||||
if (ret < 0)
|
||||
dev_err(tc35892->dev, "failed to write regs %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc35892_block_write);
|
||||
|
||||
/**
|
||||
* tc35892_set_bits() - set the value of a bitfield in a TC35892 register
|
||||
* @tc35892: Device to write to
|
||||
* @reg: Register to write
|
||||
* @mask: Mask of bits to set
|
||||
* @values: Value to set
|
||||
*/
|
||||
int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&tc35892->lock);
|
||||
|
||||
ret = tc35892_reg_read(tc35892, reg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret &= ~mask;
|
||||
ret |= val;
|
||||
|
||||
ret = tc35892_reg_write(tc35892, reg, ret);
|
||||
|
||||
out:
|
||||
mutex_unlock(&tc35892->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc35892_set_bits);
|
||||
|
||||
static struct resource gpio_resources[] = {
|
||||
{
|
||||
.start = TC35892_INT_GPIIRQ,
|
||||
.end = TC35892_INT_GPIIRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell tc35892_devs[] = {
|
||||
{
|
||||
.name = "tc35892-gpio",
|
||||
.num_resources = ARRAY_SIZE(gpio_resources),
|
||||
.resources = &gpio_resources[0],
|
||||
},
|
||||
};
|
||||
|
||||
static irqreturn_t tc35892_irq(int irq, void *data)
|
||||
{
|
||||
struct tc35892 *tc35892 = data;
|
||||
int status;
|
||||
|
||||
status = tc35892_reg_read(tc35892, TC35892_IRQST);
|
||||
if (status < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
while (status) {
|
||||
int bit = __ffs(status);
|
||||
|
||||
handle_nested_irq(tc35892->irq_base + bit);
|
||||
status &= ~(1 << bit);
|
||||
}
|
||||
|
||||
/*
|
||||
* A dummy read or write (to any register) appears to be necessary to
|
||||
* have the last interrupt clear (for example, GPIO IC write) take
|
||||
* effect.
|
||||
*/
|
||||
tc35892_reg_read(tc35892, TC35892_IRQST);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void tc35892_irq_dummy(unsigned int irq)
|
||||
{
|
||||
/* No mask/unmask at this level */
|
||||
}
|
||||
|
||||
static struct irq_chip tc35892_irq_chip = {
|
||||
.name = "tc35892",
|
||||
.mask = tc35892_irq_dummy,
|
||||
.unmask = tc35892_irq_dummy,
|
||||
};
|
||||
|
||||
static int tc35892_irq_init(struct tc35892 *tc35892)
|
||||
{
|
||||
int base = tc35892->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) {
|
||||
set_irq_chip_data(irq, tc35892);
|
||||
set_irq_chip_and_handler(irq, &tc35892_irq_chip,
|
||||
handle_edge_irq);
|
||||
set_irq_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
set_irq_noprobe(irq);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc35892_irq_remove(struct tc35892 *tc35892)
|
||||
{
|
||||
int base = tc35892->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) {
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, 0);
|
||||
#endif
|
||||
set_irq_chip_and_handler(irq, NULL, NULL);
|
||||
set_irq_chip_data(irq, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int tc35892_chip_init(struct tc35892 *tc35892)
|
||||
{
|
||||
int manf, ver, ret;
|
||||
|
||||
manf = tc35892_reg_read(tc35892, TC35892_MANFCODE);
|
||||
if (manf < 0)
|
||||
return manf;
|
||||
|
||||
ver = tc35892_reg_read(tc35892, TC35892_VERSION);
|
||||
if (ver < 0)
|
||||
return ver;
|
||||
|
||||
if (manf != TC35892_MANFCODE_MAGIC) {
|
||||
dev_err(tc35892->dev, "unknown manufacturer: %#x\n", manf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver);
|
||||
|
||||
/* Put everything except the IRQ module into reset */
|
||||
ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL,
|
||||
TC35892_RSTCTRL_TIMRST
|
||||
| TC35892_RSTCTRL_ROTRST
|
||||
| TC35892_RSTCTRL_KBDRST
|
||||
| TC35892_RSTCTRL_GPIRST);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Clear the reset interrupt. */
|
||||
return tc35892_reg_write(tc35892, TC35892_RSTINTCLR, 0x1);
|
||||
}
|
||||
|
||||
static int __devinit tc35892_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tc35892_platform_data *pdata = i2c->dev.platform_data;
|
||||
struct tc35892 *tc35892;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return -EIO;
|
||||
|
||||
tc35892 = kzalloc(sizeof(struct tc35892), GFP_KERNEL);
|
||||
if (!tc35892)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&tc35892->lock);
|
||||
|
||||
tc35892->dev = &i2c->dev;
|
||||
tc35892->i2c = i2c;
|
||||
tc35892->pdata = pdata;
|
||||
tc35892->irq_base = pdata->irq_base;
|
||||
tc35892->num_gpio = id->driver_data;
|
||||
|
||||
i2c_set_clientdata(i2c, tc35892);
|
||||
|
||||
ret = tc35892_chip_init(tc35892);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
ret = tc35892_irq_init(tc35892);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
ret = request_threaded_irq(tc35892->i2c->irq, NULL, tc35892_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"tc35892", tc35892);
|
||||
if (ret) {
|
||||
dev_err(tc35892->dev, "failed to request IRQ: %d\n", ret);
|
||||
goto out_removeirq;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(tc35892->dev, -1, tc35892_devs,
|
||||
ARRAY_SIZE(tc35892_devs), NULL,
|
||||
tc35892->irq_base);
|
||||
if (ret) {
|
||||
dev_err(tc35892->dev, "failed to add children\n");
|
||||
goto out_freeirq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_freeirq:
|
||||
free_irq(tc35892->i2c->irq, tc35892);
|
||||
out_removeirq:
|
||||
tc35892_irq_remove(tc35892);
|
||||
out_free:
|
||||
kfree(tc35892);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit tc35892_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tc35892 *tc35892 = i2c_get_clientdata(client);
|
||||
|
||||
mfd_remove_devices(tc35892->dev);
|
||||
|
||||
free_irq(tc35892->i2c->irq, tc35892);
|
||||
tc35892_irq_remove(tc35892);
|
||||
|
||||
kfree(tc35892);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tc35892_id[] = {
|
||||
{ "tc35892", 24 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tc35892_id);
|
||||
|
||||
static struct i2c_driver tc35892_driver = {
|
||||
.driver.name = "tc35892",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = tc35892_probe,
|
||||
.remove = __devexit_p(tc35892_remove),
|
||||
.id_table = tc35892_id,
|
||||
};
|
||||
|
||||
static int __init tc35892_init(void)
|
||||
{
|
||||
return i2c_add_driver(&tc35892_driver);
|
||||
}
|
||||
subsys_initcall(tc35892_init);
|
||||
|
||||
static void __exit tc35892_exit(void)
|
||||
{
|
||||
i2c_del_driver(&tc35892_driver);
|
||||
}
|
||||
module_exit(tc35892_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("TC35892 MFD core driver");
|
||||
MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
|
422
drivers/mfd/tc3589x.c
Normal file
422
drivers/mfd/tc3589x.c
Normal file
@ -0,0 +1,422 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License, version 2
|
||||
* Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/tc3589x.h>
|
||||
|
||||
#define TC3589x_CLKMODE_MODCTL_SLEEP 0x0
|
||||
#define TC3589x_CLKMODE_MODCTL_OPERATION (1 << 0)
|
||||
|
||||
/**
|
||||
* tc3589x_reg_read() - read a single TC3589x register
|
||||
* @tc3589x: Device to read from
|
||||
* @reg: Register to read
|
||||
*/
|
||||
int tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(tc3589x->i2c, reg);
|
||||
if (ret < 0)
|
||||
dev_err(tc3589x->dev, "failed to read reg %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc3589x_reg_read);
|
||||
|
||||
/**
|
||||
* tc3589x_reg_read() - write a single TC3589x register
|
||||
* @tc3589x: Device to write to
|
||||
* @reg: Register to read
|
||||
* @data: Value to write
|
||||
*/
|
||||
int tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(tc3589x->i2c, reg, data);
|
||||
if (ret < 0)
|
||||
dev_err(tc3589x->dev, "failed to write reg %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc3589x_reg_write);
|
||||
|
||||
/**
|
||||
* tc3589x_block_read() - read multiple TC3589x registers
|
||||
* @tc3589x: Device to read from
|
||||
* @reg: First register
|
||||
* @length: Number of registers
|
||||
* @values: Buffer to write to
|
||||
*/
|
||||
int tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length, u8 *values)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(tc3589x->i2c, reg, length, values);
|
||||
if (ret < 0)
|
||||
dev_err(tc3589x->dev, "failed to read regs %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc3589x_block_read);
|
||||
|
||||
/**
|
||||
* tc3589x_block_write() - write multiple TC3589x registers
|
||||
* @tc3589x: Device to write to
|
||||
* @reg: First register
|
||||
* @length: Number of registers
|
||||
* @values: Values to write
|
||||
*/
|
||||
int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length,
|
||||
const u8 *values)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(tc3589x->i2c, reg, length,
|
||||
values);
|
||||
if (ret < 0)
|
||||
dev_err(tc3589x->dev, "failed to write regs %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc3589x_block_write);
|
||||
|
||||
/**
|
||||
* tc3589x_set_bits() - set the value of a bitfield in a TC3589x register
|
||||
* @tc3589x: Device to write to
|
||||
* @reg: Register to write
|
||||
* @mask: Mask of bits to set
|
||||
* @values: Value to set
|
||||
*/
|
||||
int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&tc3589x->lock);
|
||||
|
||||
ret = tc3589x_reg_read(tc3589x, reg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret &= ~mask;
|
||||
ret |= val;
|
||||
|
||||
ret = tc3589x_reg_write(tc3589x, reg, ret);
|
||||
|
||||
out:
|
||||
mutex_unlock(&tc3589x->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc3589x_set_bits);
|
||||
|
||||
static struct resource gpio_resources[] = {
|
||||
{
|
||||
.start = TC3589x_INT_GPIIRQ,
|
||||
.end = TC3589x_INT_GPIIRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource keypad_resources[] = {
|
||||
{
|
||||
.start = TC3589x_INT_KBDIRQ,
|
||||
.end = TC3589x_INT_KBDIRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell tc3589x_dev_gpio[] = {
|
||||
{
|
||||
.name = "tc3589x-gpio",
|
||||
.num_resources = ARRAY_SIZE(gpio_resources),
|
||||
.resources = &gpio_resources[0],
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell tc3589x_dev_keypad[] = {
|
||||
{
|
||||
.name = "tc3589x-keypad",
|
||||
.num_resources = ARRAY_SIZE(keypad_resources),
|
||||
.resources = &keypad_resources[0],
|
||||
},
|
||||
};
|
||||
|
||||
static irqreturn_t tc3589x_irq(int irq, void *data)
|
||||
{
|
||||
struct tc3589x *tc3589x = data;
|
||||
int status;
|
||||
|
||||
again:
|
||||
status = tc3589x_reg_read(tc3589x, TC3589x_IRQST);
|
||||
if (status < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
while (status) {
|
||||
int bit = __ffs(status);
|
||||
|
||||
handle_nested_irq(tc3589x->irq_base + bit);
|
||||
status &= ~(1 << bit);
|
||||
}
|
||||
|
||||
/*
|
||||
* A dummy read or write (to any register) appears to be necessary to
|
||||
* have the last interrupt clear (for example, GPIO IC write) take
|
||||
* effect. In such a case, recheck for any interrupt which is still
|
||||
* pending.
|
||||
*/
|
||||
status = tc3589x_reg_read(tc3589x, TC3589x_IRQST);
|
||||
if (status)
|
||||
goto again;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tc3589x_irq_init(struct tc3589x *tc3589x)
|
||||
{
|
||||
int base = tc3589x->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) {
|
||||
set_irq_chip_data(irq, tc3589x);
|
||||
set_irq_chip_and_handler(irq, &dummy_irq_chip,
|
||||
handle_edge_irq);
|
||||
set_irq_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
set_irq_noprobe(irq);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc3589x_irq_remove(struct tc3589x *tc3589x)
|
||||
{
|
||||
int base = tc3589x->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) {
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, 0);
|
||||
#endif
|
||||
set_irq_chip_and_handler(irq, NULL, NULL);
|
||||
set_irq_chip_data(irq, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int tc3589x_chip_init(struct tc3589x *tc3589x)
|
||||
{
|
||||
int manf, ver, ret;
|
||||
|
||||
manf = tc3589x_reg_read(tc3589x, TC3589x_MANFCODE);
|
||||
if (manf < 0)
|
||||
return manf;
|
||||
|
||||
ver = tc3589x_reg_read(tc3589x, TC3589x_VERSION);
|
||||
if (ver < 0)
|
||||
return ver;
|
||||
|
||||
if (manf != TC3589x_MANFCODE_MAGIC) {
|
||||
dev_err(tc3589x->dev, "unknown manufacturer: %#x\n", manf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver);
|
||||
|
||||
/*
|
||||
* Put everything except the IRQ module into reset;
|
||||
* also spare the GPIO module for any pin initialization
|
||||
* done during pre-kernel boot
|
||||
*/
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL,
|
||||
TC3589x_RSTCTRL_TIMRST
|
||||
| TC3589x_RSTCTRL_ROTRST
|
||||
| TC3589x_RSTCTRL_KBDRST);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Clear the reset interrupt. */
|
||||
return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1);
|
||||
}
|
||||
|
||||
static int __devinit tc3589x_device_init(struct tc3589x *tc3589x)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int blocks = tc3589x->pdata->block;
|
||||
|
||||
if (blocks & TC3589x_BLOCK_GPIO) {
|
||||
ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio,
|
||||
ARRAY_SIZE(tc3589x_dev_gpio), NULL,
|
||||
tc3589x->irq_base);
|
||||
if (ret) {
|
||||
dev_err(tc3589x->dev, "failed to add gpio child\n");
|
||||
return ret;
|
||||
}
|
||||
dev_info(tc3589x->dev, "added gpio block\n");
|
||||
}
|
||||
|
||||
if (blocks & TC3589x_BLOCK_KEYPAD) {
|
||||
ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad,
|
||||
ARRAY_SIZE(tc3589x_dev_keypad), NULL,
|
||||
tc3589x->irq_base);
|
||||
if (ret) {
|
||||
dev_err(tc3589x->dev, "failed to keypad child\n");
|
||||
return ret;
|
||||
}
|
||||
dev_info(tc3589x->dev, "added keypad block\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit tc3589x_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tc3589x_platform_data *pdata = i2c->dev.platform_data;
|
||||
struct tc3589x *tc3589x;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return -EIO;
|
||||
|
||||
tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL);
|
||||
if (!tc3589x)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&tc3589x->lock);
|
||||
|
||||
tc3589x->dev = &i2c->dev;
|
||||
tc3589x->i2c = i2c;
|
||||
tc3589x->pdata = pdata;
|
||||
tc3589x->irq_base = pdata->irq_base;
|
||||
tc3589x->num_gpio = id->driver_data;
|
||||
|
||||
i2c_set_clientdata(i2c, tc3589x);
|
||||
|
||||
ret = tc3589x_chip_init(tc3589x);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
ret = tc3589x_irq_init(tc3589x);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"tc3589x", tc3589x);
|
||||
if (ret) {
|
||||
dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret);
|
||||
goto out_removeirq;
|
||||
}
|
||||
|
||||
ret = tc3589x_device_init(tc3589x);
|
||||
if (ret) {
|
||||
dev_err(tc3589x->dev, "failed to add child devices\n");
|
||||
goto out_freeirq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_freeirq:
|
||||
free_irq(tc3589x->i2c->irq, tc3589x);
|
||||
out_removeirq:
|
||||
tc3589x_irq_remove(tc3589x);
|
||||
out_free:
|
||||
kfree(tc3589x);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit tc3589x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tc3589x *tc3589x = i2c_get_clientdata(client);
|
||||
|
||||
mfd_remove_devices(tc3589x->dev);
|
||||
|
||||
free_irq(tc3589x->i2c->irq, tc3589x);
|
||||
tc3589x_irq_remove(tc3589x);
|
||||
|
||||
kfree(tc3589x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc3589x_suspend(struct device *dev)
|
||||
{
|
||||
struct tc3589x *tc3589x = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = tc3589x->i2c;
|
||||
int ret = 0;
|
||||
|
||||
/* put the system to sleep mode */
|
||||
if (!device_may_wakeup(&client->dev))
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
|
||||
TC3589x_CLKMODE_MODCTL_SLEEP);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tc3589x_resume(struct device *dev)
|
||||
{
|
||||
struct tc3589x *tc3589x = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = tc3589x->i2c;
|
||||
int ret = 0;
|
||||
|
||||
/* enable the system into operation */
|
||||
if (!device_may_wakeup(&client->dev))
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
|
||||
TC3589x_CLKMODE_MODCTL_OPERATION);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend,
|
||||
tc3589x_resume);
|
||||
|
||||
static const struct i2c_device_id tc3589x_id[] = {
|
||||
{ "tc3589x", 24 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tc3589x_id);
|
||||
|
||||
static struct i2c_driver tc3589x_driver = {
|
||||
.driver.name = "tc3589x",
|
||||
.driver.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.driver.pm = &tc3589x_dev_pm_ops,
|
||||
#endif
|
||||
.probe = tc3589x_probe,
|
||||
.remove = __devexit_p(tc3589x_remove),
|
||||
.id_table = tc3589x_id,
|
||||
};
|
||||
|
||||
static int __init tc3589x_init(void)
|
||||
{
|
||||
return i2c_add_driver(&tc3589x_driver);
|
||||
}
|
||||
subsys_initcall(tc3589x_init);
|
||||
|
||||
static void __exit tc3589x_exit(void)
|
||||
{
|
||||
i2c_del_driver(&tc3589x_driver);
|
||||
}
|
||||
module_exit(tc3589x_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("TC3589x MFD core driver");
|
||||
MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
|
@ -11,7 +11,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <mach/mbox.h>
|
||||
#include <mach/mbox-db5500.h>
|
||||
#include <net/caif/caif_shm.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License, version 2
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_TC35892_H
|
||||
#define __LINUX_MFD_TC35892_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
#define TC35892_RSTCTRL_IRQRST (1 << 4)
|
||||
#define TC35892_RSTCTRL_TIMRST (1 << 3)
|
||||
#define TC35892_RSTCTRL_ROTRST (1 << 2)
|
||||
#define TC35892_RSTCTRL_KBDRST (1 << 1)
|
||||
#define TC35892_RSTCTRL_GPIRST (1 << 0)
|
||||
|
||||
#define TC35892_IRQST 0x91
|
||||
|
||||
#define TC35892_MANFCODE_MAGIC 0x03
|
||||
#define TC35892_MANFCODE 0x80
|
||||
#define TC35892_VERSION 0x81
|
||||
#define TC35892_IOCFG 0xA7
|
||||
|
||||
#define TC35892_CLKMODE 0x88
|
||||
#define TC35892_CLKCFG 0x89
|
||||
#define TC35892_CLKEN 0x8A
|
||||
|
||||
#define TC35892_RSTCTRL 0x82
|
||||
#define TC35892_EXTRSTN 0x83
|
||||
#define TC35892_RSTINTCLR 0x84
|
||||
|
||||
#define TC35892_GPIOIS0 0xC9
|
||||
#define TC35892_GPIOIS1 0xCA
|
||||
#define TC35892_GPIOIS2 0xCB
|
||||
#define TC35892_GPIOIBE0 0xCC
|
||||
#define TC35892_GPIOIBE1 0xCD
|
||||
#define TC35892_GPIOIBE2 0xCE
|
||||
#define TC35892_GPIOIEV0 0xCF
|
||||
#define TC35892_GPIOIEV1 0xD0
|
||||
#define TC35892_GPIOIEV2 0xD1
|
||||
#define TC35892_GPIOIE0 0xD2
|
||||
#define TC35892_GPIOIE1 0xD3
|
||||
#define TC35892_GPIOIE2 0xD4
|
||||
#define TC35892_GPIORIS0 0xD6
|
||||
#define TC35892_GPIORIS1 0xD7
|
||||
#define TC35892_GPIORIS2 0xD8
|
||||
#define TC35892_GPIOMIS0 0xD9
|
||||
#define TC35892_GPIOMIS1 0xDA
|
||||
#define TC35892_GPIOMIS2 0xDB
|
||||
#define TC35892_GPIOIC0 0xDC
|
||||
#define TC35892_GPIOIC1 0xDD
|
||||
#define TC35892_GPIOIC2 0xDE
|
||||
|
||||
#define TC35892_GPIODATA0 0xC0
|
||||
#define TC35892_GPIOMASK0 0xc1
|
||||
#define TC35892_GPIODATA1 0xC2
|
||||
#define TC35892_GPIOMASK1 0xc3
|
||||
#define TC35892_GPIODATA2 0xC4
|
||||
#define TC35892_GPIOMASK2 0xC5
|
||||
|
||||
#define TC35892_GPIODIR0 0xC6
|
||||
#define TC35892_GPIODIR1 0xC7
|
||||
#define TC35892_GPIODIR2 0xC8
|
||||
|
||||
#define TC35892_GPIOSYNC0 0xE6
|
||||
#define TC35892_GPIOSYNC1 0xE7
|
||||
#define TC35892_GPIOSYNC2 0xE8
|
||||
|
||||
#define TC35892_GPIOWAKE0 0xE9
|
||||
#define TC35892_GPIOWAKE1 0xEA
|
||||
#define TC35892_GPIOWAKE2 0xEB
|
||||
|
||||
#define TC35892_GPIOODM0 0xE0
|
||||
#define TC35892_GPIOODE0 0xE1
|
||||
#define TC35892_GPIOODM1 0xE2
|
||||
#define TC35892_GPIOODE1 0xE3
|
||||
#define TC35892_GPIOODM2 0xE4
|
||||
#define TC35892_GPIOODE2 0xE5
|
||||
|
||||
#define TC35892_INT_GPIIRQ 0
|
||||
#define TC35892_INT_TI0IRQ 1
|
||||
#define TC35892_INT_TI1IRQ 2
|
||||
#define TC35892_INT_TI2IRQ 3
|
||||
#define TC35892_INT_ROTIRQ 5
|
||||
#define TC35892_INT_KBDIRQ 6
|
||||
#define TC35892_INT_PORIRQ 7
|
||||
|
||||
#define TC35892_NR_INTERNAL_IRQS 8
|
||||
#define TC35892_INT_GPIO(x) (TC35892_NR_INTERNAL_IRQS + (x))
|
||||
|
||||
struct tc35892 {
|
||||
struct mutex lock;
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c;
|
||||
|
||||
int irq_base;
|
||||
int num_gpio;
|
||||
struct tc35892_platform_data *pdata;
|
||||
};
|
||||
|
||||
extern int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data);
|
||||
extern int tc35892_reg_read(struct tc35892 *tc35892, u8 reg);
|
||||
extern int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length,
|
||||
u8 *values);
|
||||
extern int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length,
|
||||
const u8 *values);
|
||||
extern int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val);
|
||||
|
||||
/**
|
||||
* struct tc35892_gpio_platform_data - TC35892 GPIO platform data
|
||||
* @gpio_base: first gpio number assigned to TC35892. A maximum of
|
||||
* %TC35892_NR_GPIOS GPIOs will be allocated.
|
||||
* @setup: callback for board-specific initialization
|
||||
* @remove: callback for board-specific teardown
|
||||
*/
|
||||
struct tc35892_gpio_platform_data {
|
||||
int gpio_base;
|
||||
void (*setup)(struct tc35892 *tc35892, unsigned gpio_base);
|
||||
void (*remove)(struct tc35892 *tc35892, unsigned gpio_base);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tc35892_platform_data - TC35892 platform data
|
||||
* @irq_base: base IRQ number. %TC35892_NR_IRQS irqs will be used.
|
||||
* @gpio: GPIO-specific platform data
|
||||
*/
|
||||
struct tc35892_platform_data {
|
||||
int irq_base;
|
||||
struct tc35892_gpio_platform_data *gpio;
|
||||
};
|
||||
|
||||
#define TC35892_NR_GPIOS 24
|
||||
#define TC35892_NR_IRQS TC35892_INT_GPIO(TC35892_NR_GPIOS)
|
||||
|
||||
#endif
|
195
include/linux/mfd/tc3589x.h
Normal file
195
include/linux/mfd/tc3589x.h
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License, version 2
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_TC3589x_H
|
||||
#define __LINUX_MFD_TC3589x_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
enum tx3589x_block {
|
||||
TC3589x_BLOCK_GPIO = 1 << 0,
|
||||
TC3589x_BLOCK_KEYPAD = 1 << 1,
|
||||
};
|
||||
|
||||
#define TC3589x_RSTCTRL_IRQRST (1 << 4)
|
||||
#define TC3589x_RSTCTRL_TIMRST (1 << 3)
|
||||
#define TC3589x_RSTCTRL_ROTRST (1 << 2)
|
||||
#define TC3589x_RSTCTRL_KBDRST (1 << 1)
|
||||
#define TC3589x_RSTCTRL_GPIRST (1 << 0)
|
||||
|
||||
/* Keyboard Configuration Registers */
|
||||
#define TC3589x_KBDSETTLE_REG 0x01
|
||||
#define TC3589x_KBDBOUNCE 0x02
|
||||
#define TC3589x_KBDSIZE 0x03
|
||||
#define TC3589x_KBCFG_LSB 0x04
|
||||
#define TC3589x_KBCFG_MSB 0x05
|
||||
#define TC3589x_KBDIC 0x08
|
||||
#define TC3589x_KBDMSK 0x09
|
||||
#define TC3589x_EVTCODE_FIFO 0x10
|
||||
#define TC3589x_KBDMFS 0x8F
|
||||
|
||||
#define TC3589x_IRQST 0x91
|
||||
|
||||
#define TC3589x_MANFCODE_MAGIC 0x03
|
||||
#define TC3589x_MANFCODE 0x80
|
||||
#define TC3589x_VERSION 0x81
|
||||
#define TC3589x_IOCFG 0xA7
|
||||
|
||||
#define TC3589x_CLKMODE 0x88
|
||||
#define TC3589x_CLKCFG 0x89
|
||||
#define TC3589x_CLKEN 0x8A
|
||||
|
||||
#define TC3589x_RSTCTRL 0x82
|
||||
#define TC3589x_EXTRSTN 0x83
|
||||
#define TC3589x_RSTINTCLR 0x84
|
||||
|
||||
/* Pull up/down configuration registers */
|
||||
#define TC3589x_IOCFG 0xA7
|
||||
#define TC3589x_IOPULLCFG0_LSB 0xAA
|
||||
#define TC3589x_IOPULLCFG0_MSB 0xAB
|
||||
#define TC3589x_IOPULLCFG1_LSB 0xAC
|
||||
#define TC3589x_IOPULLCFG1_MSB 0xAD
|
||||
#define TC3589x_IOPULLCFG2_LSB 0xAE
|
||||
|
||||
#define TC3589x_GPIOIS0 0xC9
|
||||
#define TC3589x_GPIOIS1 0xCA
|
||||
#define TC3589x_GPIOIS2 0xCB
|
||||
#define TC3589x_GPIOIBE0 0xCC
|
||||
#define TC3589x_GPIOIBE1 0xCD
|
||||
#define TC3589x_GPIOIBE2 0xCE
|
||||
#define TC3589x_GPIOIEV0 0xCF
|
||||
#define TC3589x_GPIOIEV1 0xD0
|
||||
#define TC3589x_GPIOIEV2 0xD1
|
||||
#define TC3589x_GPIOIE0 0xD2
|
||||
#define TC3589x_GPIOIE1 0xD3
|
||||
#define TC3589x_GPIOIE2 0xD4
|
||||
#define TC3589x_GPIORIS0 0xD6
|
||||
#define TC3589x_GPIORIS1 0xD7
|
||||
#define TC3589x_GPIORIS2 0xD8
|
||||
#define TC3589x_GPIOMIS0 0xD9
|
||||
#define TC3589x_GPIOMIS1 0xDA
|
||||
#define TC3589x_GPIOMIS2 0xDB
|
||||
#define TC3589x_GPIOIC0 0xDC
|
||||
#define TC3589x_GPIOIC1 0xDD
|
||||
#define TC3589x_GPIOIC2 0xDE
|
||||
|
||||
#define TC3589x_GPIODATA0 0xC0
|
||||
#define TC3589x_GPIOMASK0 0xc1
|
||||
#define TC3589x_GPIODATA1 0xC2
|
||||
#define TC3589x_GPIOMASK1 0xc3
|
||||
#define TC3589x_GPIODATA2 0xC4
|
||||
#define TC3589x_GPIOMASK2 0xC5
|
||||
|
||||
#define TC3589x_GPIODIR0 0xC6
|
||||
#define TC3589x_GPIODIR1 0xC7
|
||||
#define TC3589x_GPIODIR2 0xC8
|
||||
|
||||
#define TC3589x_GPIOSYNC0 0xE6
|
||||
#define TC3589x_GPIOSYNC1 0xE7
|
||||
#define TC3589x_GPIOSYNC2 0xE8
|
||||
|
||||
#define TC3589x_GPIOWAKE0 0xE9
|
||||
#define TC3589x_GPIOWAKE1 0xEA
|
||||
#define TC3589x_GPIOWAKE2 0xEB
|
||||
|
||||
#define TC3589x_GPIOODM0 0xE0
|
||||
#define TC3589x_GPIOODE0 0xE1
|
||||
#define TC3589x_GPIOODM1 0xE2
|
||||
#define TC3589x_GPIOODE1 0xE3
|
||||
#define TC3589x_GPIOODM2 0xE4
|
||||
#define TC3589x_GPIOODE2 0xE5
|
||||
|
||||
#define TC3589x_INT_GPIIRQ 0
|
||||
#define TC3589x_INT_TI0IRQ 1
|
||||
#define TC3589x_INT_TI1IRQ 2
|
||||
#define TC3589x_INT_TI2IRQ 3
|
||||
#define TC3589x_INT_ROTIRQ 5
|
||||
#define TC3589x_INT_KBDIRQ 6
|
||||
#define TC3589x_INT_PORIRQ 7
|
||||
|
||||
#define TC3589x_NR_INTERNAL_IRQS 8
|
||||
#define TC3589x_INT_GPIO(x) (TC3589x_NR_INTERNAL_IRQS + (x))
|
||||
|
||||
struct tc3589x {
|
||||
struct mutex lock;
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c;
|
||||
|
||||
int irq_base;
|
||||
int num_gpio;
|
||||
struct tc3589x_platform_data *pdata;
|
||||
};
|
||||
|
||||
extern int tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data);
|
||||
extern int tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg);
|
||||
extern int tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length,
|
||||
u8 *values);
|
||||
extern int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length,
|
||||
const u8 *values);
|
||||
extern int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val);
|
||||
|
||||
/*
|
||||
* Keypad related platform specific constants
|
||||
* These values may be modified for fine tuning
|
||||
*/
|
||||
#define TC_KPD_ROWS 0x8
|
||||
#define TC_KPD_COLUMNS 0x8
|
||||
#define TC_KPD_DEBOUNCE_PERIOD 0xA3
|
||||
#define TC_KPD_SETTLE_TIME 0xA3
|
||||
|
||||
/**
|
||||
* struct tc35893_platform_data - data structure for platform specific data
|
||||
* @keymap_data: matrix scan code table for keycodes
|
||||
* @krow: mask for available rows, value is 0xFF
|
||||
* @kcol: mask for available columns, value is 0xFF
|
||||
* @debounce_period: platform specific debounce time
|
||||
* @settle_time: platform specific settle down time
|
||||
* @irqtype: type of interrupt, falling or rising edge
|
||||
* @enable_wakeup: specifies if keypad event can wake up system from sleep
|
||||
* @no_autorepeat: flag for auto repetition
|
||||
*/
|
||||
struct tc3589x_keypad_platform_data {
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
u8 krow;
|
||||
u8 kcol;
|
||||
u8 debounce_period;
|
||||
u8 settle_time;
|
||||
unsigned long irqtype;
|
||||
bool enable_wakeup;
|
||||
bool no_autorepeat;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tc3589x_gpio_platform_data - TC3589x GPIO platform data
|
||||
* @gpio_base: first gpio number assigned to TC3589x. A maximum of
|
||||
* %TC3589x_NR_GPIOS GPIOs will be allocated.
|
||||
* @setup: callback for board-specific initialization
|
||||
* @remove: callback for board-specific teardown
|
||||
*/
|
||||
struct tc3589x_gpio_platform_data {
|
||||
int gpio_base;
|
||||
void (*setup)(struct tc3589x *tc3589x, unsigned gpio_base);
|
||||
void (*remove)(struct tc3589x *tc3589x, unsigned gpio_base);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tc3589x_platform_data - TC3589x platform data
|
||||
* @block: bitmask of blocks to enable (use TC3589x_BLOCK_*)
|
||||
* @irq_base: base IRQ number. %TC3589x_NR_IRQS irqs will be used.
|
||||
* @gpio: GPIO-specific platform data
|
||||
* @keypad: keypad-specific platform data
|
||||
*/
|
||||
struct tc3589x_platform_data {
|
||||
unsigned int block;
|
||||
int irq_base;
|
||||
struct tc3589x_gpio_platform_data *gpio;
|
||||
const struct tc3589x_keypad_platform_data *keypad;
|
||||
};
|
||||
|
||||
#define TC3589x_NR_GPIOS 24
|
||||
#define TC3589x_NR_IRQS TC3589x_INT_GPIO(TC3589x_NR_GPIOS)
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user