diff options
Diffstat (limited to 'arch/arm/mach-omap1')
-rw-r--r-- | arch/arm/mach-omap1/Makefile | 7 | ||||
-rw-r--r-- | arch/arm/mach-omap1/board-h2.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap1/board-h3.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-omap1/board-nokia770.c | 17 | ||||
-rw-r--r-- | arch/arm/mach-omap1/board-osk.c | 157 | ||||
-rw-r--r-- | arch/arm/mach-omap1/board-palmte.c | 12 | ||||
-rw-r--r-- | arch/arm/mach-omap1/board-palmz71.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap1/fpga.c | 10 | ||||
-rw-r--r-- | arch/arm/mach-omap1/leds-osk.c | 80 | ||||
-rw-r--r-- | arch/arm/mach-omap1/mcbsp.c | 280 | ||||
-rw-r--r-- | arch/arm/mach-omap1/mux.c | 146 | ||||
-rw-r--r-- | arch/arm/mach-omap1/pm.c | 7 | ||||
-rw-r--r-- | arch/arm/mach-omap1/sram.S | 57 | ||||
-rw-r--r-- | arch/arm/mach-omap1/time.c | 49 | ||||
-rw-r--r-- | arch/arm/mach-omap1/timer32k.c | 209 |
15 files changed, 803 insertions, 235 deletions
diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile index 015a66b..1bda8f5 100644 --- a/arch/arm/mach-omap1/Makefile +++ b/arch/arm/mach-omap1/Makefile @@ -3,9 +3,12 @@ # # Common support -obj-y := io.o id.o clock.o irq.o mux.o serial.o devices.o +obj-y := io.o id.o sram.o clock.o irq.o mux.o serial.o devices.o -obj-$(CONFIG_OMAP_MPU_TIMER) += time.o +obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o + +obj-$(CONFIG_OMAP_MPU_TIMER) += time.o +obj-$(CONFIG_OMAP_32K_TIMER) += timer32k.o # Power Management obj-$(CONFIG_PM) += pm.o sleep.o diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c index 5079877..4b444fd 100644 --- a/arch/arm/mach-omap1/board-h2.c +++ b/arch/arm/mach-omap1/board-h2.c @@ -351,11 +351,9 @@ static void __init h2_init_smc91x(void) static struct i2c_board_info __initdata h2_i2c_board_info[] = { { I2C_BOARD_INFO("tps65010", 0x48), - .type = "tps65010", .irq = OMAP_GPIO_IRQ(58), }, { I2C_BOARD_INFO("isp1301_omap", 0x2d), - .type = "isp1301_omap", .irq = OMAP_GPIO_IRQ(2), }, }; diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c index c3ef1ee..7fbaa8d 100644 --- a/arch/arm/mach-omap1/board-h3.c +++ b/arch/arm/mach-omap1/board-h3.c @@ -473,8 +473,7 @@ static struct omap_board_config_kernel h3_config[] __initdata = { static struct i2c_board_info __initdata h3_i2c_board_info[] = { { - I2C_BOARD_INFO("tps65010", 0x48), - .type = "tps65013", + I2C_BOARD_INFO("tps65013", 0x48), /* .irq = OMAP_GPIO_IRQ(??), */ }, }; diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c index bcb984f..3f39e0e 100644 --- a/arch/arm/mach-omap1/board-nokia770.c +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -10,6 +10,7 @@ #include <linux/kernel.h> #include <linux/init.h> +#include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/clk.h> @@ -202,7 +203,7 @@ static struct omap_board_config_kernel nokia770_config[] __initdata = { #define AMPLIFIER_CTRL_GPIO 58 static struct clk *dspxor_ck; -static DECLARE_MUTEX(audio_pwr_sem); +static DEFINE_MUTEX(audio_pwr_lock); /* * audio_pwr_state * +--+-------------------------+---------------------------------------+ @@ -218,7 +219,7 @@ static DECLARE_MUTEX(audio_pwr_sem); static int audio_pwr_state = -1; /* - * audio_pwr_up / down should be called under audio_pwr_sem + * audio_pwr_up / down should be called under audio_pwr_lock */ static void nokia770_audio_pwr_up(void) { @@ -237,11 +238,11 @@ static void nokia770_audio_pwr_up(void) static void codec_delayed_power_down(struct work_struct *work) { - down(&audio_pwr_sem); + mutex_lock(&audio_pwr_lock); if (audio_pwr_state == -1) aic23_power_down(); clk_disable(dspxor_ck); - up(&audio_pwr_sem); + mutex_unlock(&audio_pwr_lock); } static DECLARE_DELAYED_WORK(codec_power_down_work, codec_delayed_power_down); @@ -258,19 +259,19 @@ static void nokia770_audio_pwr_down(void) static int nokia770_audio_pwr_up_request(struct dsp_kfunc_device *kdev, int stage) { - down(&audio_pwr_sem); + mutex_lock(&audio_pwr_lock); if (audio_pwr_state == -1) nokia770_audio_pwr_up(); /* force audio_pwr_state = 0, even if it was 1. */ audio_pwr_state = 0; - up(&audio_pwr_sem); + mutex_unlock(&audio_pwr_lock); return 0; } static int nokia770_audio_pwr_down_request(struct dsp_kfunc_device *kdev, int stage) { - down(&audio_pwr_sem); + mutex_lock(&audio_pwr_lock); switch (stage) { case 1: if (audio_pwr_state == 0) @@ -283,7 +284,7 @@ nokia770_audio_pwr_down_request(struct dsp_kfunc_device *kdev, int stage) } break; } - up(&audio_pwr_sem); + mutex_unlock(&audio_pwr_lock); return 0; } diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 5279e35..845c663 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -32,6 +32,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/i2c.h> +#include <linux/leds.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -183,11 +184,79 @@ static struct platform_device *osk5912_devices[] __initdata = { &osk5912_mcbsp1_device, }; +static struct gpio_led tps_leds[] = { + /* NOTE: D9 and D2 have hardware blink support. + * Also, D9 requires non-battery power. + */ + { .gpio = OSK_TPS_GPIO_LED_D9, .name = "d9", }, + { .gpio = OSK_TPS_GPIO_LED_D2, .name = "d2", }, + { .gpio = OSK_TPS_GPIO_LED_D3, .name = "d3", .active_low = 1, + .default_trigger = "heartbeat", }, +}; + +static struct gpio_led_platform_data tps_leds_data = { + .num_leds = 3, + .leds = tps_leds, +}; + +static struct platform_device osk5912_tps_leds = { + .name = "leds-gpio", + .id = 0, + .dev.platform_data = &tps_leds_data, +}; + +static int osk_tps_setup(struct i2c_client *client, void *context) +{ + /* Set GPIO 1 HIGH to disable VBUS power supply; + * OHCI driver powers it up/down as needed. + */ + gpio_request(OSK_TPS_GPIO_USB_PWR_EN, "n_vbus_en"); + gpio_direction_output(OSK_TPS_GPIO_USB_PWR_EN, 1); + + /* Set GPIO 2 high so LED D3 is off by default */ + tps65010_set_gpio_out_value(GPIO2, HIGH); + + /* Set GPIO 3 low to take ethernet out of reset */ + gpio_request(OSK_TPS_GPIO_LAN_RESET, "smc_reset"); + gpio_direction_output(OSK_TPS_GPIO_LAN_RESET, 0); + + /* GPIO4 is VDD_DSP */ + gpio_request(OSK_TPS_GPIO_DSP_PWR_EN, "dsp_power"); + gpio_direction_output(OSK_TPS_GPIO_DSP_PWR_EN, 1); + /* REVISIT if DSP support isn't configured, power it off ... */ + + /* Let LED1 (D9) blink; leds-gpio may override it */ + tps65010_set_led(LED1, BLINK); + + /* Set LED2 off by default */ + tps65010_set_led(LED2, OFF); + + /* Enable LOW_PWR handshake */ + tps65010_set_low_pwr(ON); + + /* Switch VLDO2 to 3.0V for AIC23 */ + tps65010_config_vregs1(TPS_LDO2_ENABLE | TPS_VLDO2_3_0V + | TPS_LDO1_ENABLE); + + /* register these three LEDs */ + osk5912_tps_leds.dev.parent = &client->dev; + platform_device_register(&osk5912_tps_leds); + + return 0; +} + +static struct tps65010_board tps_board = { + .base = OSK_TPS_GPIO_BASE, + .outmask = 0x0f, + .setup = osk_tps_setup, +}; + static struct i2c_board_info __initdata osk_i2c_board_info[] = { { I2C_BOARD_INFO("tps65010", 0x48), - .type = "tps65010", .irq = OMAP_GPIO_IRQ(OMAP_MPUIO(1)), + .platform_data = &tps_board, + }, /* TODO when driver support is ready: * - aic23 audio chip at 0x1a @@ -198,19 +267,23 @@ static struct i2c_board_info __initdata osk_i2c_board_info[] = { static void __init osk_init_smc91x(void) { - if ((omap_request_gpio(0)) < 0) { + u32 l; + + if ((gpio_request(0, "smc_irq")) < 0) { printk("Error requesting gpio 0 for smc91x irq\n"); return; } /* Check EMIFS wait states to fix errors with SMC_GET_PKT_HDR */ - EMIFS_CCS(1) |= 0x3; + l = omap_readl(EMIFS_CCS(1)); + l |= 0x3; + omap_writel(l, EMIFS_CCS(1)); } static void __init osk_init_cf(void) { omap_cfg_reg(M7_1610_GPIO62); - if ((omap_request_gpio(62)) < 0) { + if ((gpio_request(62, "cf_irq")) < 0) { printk("Error requesting gpio 62 for CF irq\n"); return; } @@ -334,7 +407,7 @@ static struct platform_device *mistral_devices[] __initdata = { static int mistral_get_pendown_state(void) { - return !omap_get_gpio_datain(4); + return !gpio_get_value(4); } static const struct ads7846_platform_data mistral_ts_info = { @@ -396,25 +469,31 @@ static void __init osk_mistral_init(void) omap_cfg_reg(W14_1610_CCP_DATAP); /* CAM_PWDN */ - if (omap_request_gpio(11) == 0) { + if (gpio_request(11, "cam_pwdn") == 0) { omap_cfg_reg(N20_1610_GPIO11); - omap_set_gpio_direction(11, 0 /* out */); - omap_set_gpio_dataout(11, 0 /* off */); + gpio_direction_output(11, 0); } else pr_debug("OSK+Mistral: CAM_PWDN is awol\n"); /* omap_cfg_reg(P19_1610_GPIO6); */ /* BUSY */ + gpio_request(6, "ts_busy"); + gpio_direction_input(6); + omap_cfg_reg(P20_1610_GPIO4); /* PENIRQ */ + gpio_request(4, "ts_int"); + gpio_direction_input(4); set_irq_type(OMAP_GPIO_IRQ(4), IRQT_FALLING); + spi_register_board_info(mistral_boardinfo, ARRAY_SIZE(mistral_boardinfo)); /* the sideways button (SW1) is for use as a "wakeup" button */ omap_cfg_reg(N15_1610_MPUIO2); - if (omap_request_gpio(OMAP_MPUIO(2)) == 0) { + if (gpio_request(OMAP_MPUIO(2), "wakeup") == 0) { int ret = 0; - omap_set_gpio_direction(OMAP_MPUIO(2), 1); + + gpio_direction_input(OMAP_MPUIO(2)); set_irq_type(OMAP_GPIO_IRQ(OMAP_MPUIO(2)), IRQT_RISING); #ifdef CONFIG_PM /* share the IRQ in case someone wants to use the @@ -425,7 +504,7 @@ static void __init osk_mistral_init(void) IRQF_SHARED, "mistral_wakeup", &osk_mistral_wake_interrupt); if (ret != 0) { - omap_free_gpio(OMAP_MPUIO(2)); + gpio_free(OMAP_MPUIO(2)); printk(KERN_ERR "OSK+Mistral: no wakeup irq, %d?\n", ret); } else @@ -438,10 +517,8 @@ static void __init osk_mistral_init(void) * board, like the touchscreen, EEPROM, and wakeup (!) switch. */ omap_cfg_reg(PWL); - if (omap_request_gpio(2) == 0) { - omap_set_gpio_direction(2, 0 /* out */); - omap_set_gpio_dataout(2, 1 /* on */); - } + if (gpio_request(2, "lcd_pwr") == 0) + gpio_direction_output(2, 1); platform_add_devices(mistral_devices, ARRAY_SIZE(mistral_devices)); } @@ -453,20 +530,26 @@ static void __init osk_mistral_init(void) { } static void __init osk_init(void) { + u32 l; + /* Workaround for wrong CS3 (NOR flash) timing * There are some U-Boot versions out there which configure * wrong CS3 memory timings. This mainly leads to CRC * or similar errors if you use NOR flash (e.g. with JFFS2) */ - if (EMIFS_CCS(3) != EMIFS_CS3_VAL) - EMIFS_CCS(3) = EMIFS_CS3_VAL; + l = omap_readl(EMIFS_CCS(3)); + if (l != EMIFS_CS3_VAL) + omap_writel(EMIFS_CS3_VAL, EMIFS_CCS(3)); osk_flash_resource.end = osk_flash_resource.start = omap_cs3_phys(); osk_flash_resource.end += SZ_32M - 1; platform_add_devices(osk5912_devices, ARRAY_SIZE(osk5912_devices)); omap_board_config = osk_config; omap_board_config_size = ARRAY_SIZE(osk_config); - USB_TRANSCEIVER_CTRL_REG |= (3 << 1); + + l = omap_readl(USB_TRANSCEIVER_CTRL); + l |= (3 << 1); + omap_writel(l, USB_TRANSCEIVER_CTRL); /* irq for tps65010 chip */ /* bootloader effectively does: omap_cfg_reg(U19_1610_MPUIO1); */ @@ -484,44 +567,6 @@ static void __init osk_map_io(void) omap1_map_common_io(); } -#ifdef CONFIG_TPS65010 -static int __init osk_tps_init(void) -{ - if (!machine_is_omap_osk()) - return 0; - - /* Let LED1 (D9) blink */ - tps65010_set_led(LED1, BLINK); - - /* Disable LED 2 (D2) */ - tps65010_set_led(LED2, OFF); - - /* Set GPIO 1 HIGH to disable VBUS power supply; - * OHCI driver powers it up/down as needed. - */ - tps65010_set_gpio_out_value(GPIO1, HIGH); - - /* Set GPIO 2 low to turn on LED D3 */ - tps65010_set_gpio_out_value(GPIO2, HIGH); - - /* Set GPIO 3 low to take ethernet out of reset */ - tps65010_set_gpio_out_value(GPIO3, LOW); - - /* gpio4 for VDD_DSP */ - /* FIXME send power to DSP iff it's configured */ - - /* Enable LOW_PWR */ - tps65010_set_low_pwr(ON); - - /* Switch VLDO2 to 3.0V for AIC23 */ - tps65010_config_vregs1(TPS_LDO2_ENABLE | TPS_VLDO2_3_0V - | TPS_LDO1_ENABLE); - - return 0; -} -fs_initcall(osk_tps_init); -#endif - MACHINE_START(OMAP_OSK, "TI-OSK") /* Maintainer: Dirk Behme <dirk.behme@de.bosch.com> */ .phys_io = 0xfff00000, diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c index ca1a4bf..a4d2012 100644 --- a/arch/arm/mach-omap1/board-palmte.c +++ b/arch/arm/mach-omap1/board-palmte.c @@ -24,7 +24,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/spi/spi.h> -#include <linux/spi/tsc2102.h> #include <linux/interrupt.h> #include <linux/apm-emulation.h> @@ -63,7 +62,7 @@ static const int palmte_keymap[] = { KEY(1, 1, KEY_DOWN), KEY(1, 2, KEY_UP), KEY(1, 3, KEY_RIGHT), - KEY(1, 4, KEY_CENTER), + KEY(1, 4, KEY_ENTER), 0, }; @@ -315,14 +314,6 @@ static void palmte_get_power_status(struct apm_power_info *info, int *battery) #define palmte_get_power_status NULL #endif -static struct tsc2102_config palmte_tsc2102_config = { - .use_internal = 0, - .monitor = TSC_BAT1 | TSC_AUX | TSC_TEMP, - .temp_at25c = { 2200, 2615 }, - .apm_report = palmte_get_power_status, - .alsa_config = &palmte_alsa_config, -}; - static struct omap_board_config_kernel palmte_config[] __initdata = { { OMAP_TAG_USB, &palmte_usb_config }, { OMAP_TAG_MMC, &palmte_mmc_config }, @@ -336,7 +327,6 @@ static struct spi_board_info palmte_spi_info[] __initdata = { .bus_num = 2, /* uWire (officially) */ .chip_select = 0, /* As opposed to 3 */ .irq = OMAP_GPIO_IRQ(PALMTE_PINTDAV_GPIO), - .platform_data = &palmte_tsc2102_config, .max_speed_hz = 8000000, }, }; diff --git a/arch/arm/mach-omap1/board-palmz71.c b/arch/arm/mach-omap1/board-palmz71.c index 1565107..e020c27 100644 --- a/arch/arm/mach-omap1/board-palmz71.c +++ b/arch/arm/mach-omap1/board-palmz71.c @@ -65,7 +65,7 @@ static int palmz71_keymap[] = { KEY(1, 1, KEY_DOWN), KEY(1, 2, KEY_UP), KEY(1, 3, KEY_RIGHT), - KEY(1, 4, KEY_CENTER), + KEY(1, 4, KEY_ENTER), KEY(2, 0, KEY_CAMERA), 0, }; diff --git a/arch/arm/mach-omap1/fpga.c b/arch/arm/mach-omap1/fpga.c index 30e1881..0cf62ef 100644 --- a/arch/arm/mach-omap1/fpga.c +++ b/arch/arm/mach-omap1/fpga.c @@ -32,7 +32,7 @@ static void fpga_mask_irq(unsigned int irq) { - irq -= OMAP1510_IH_FPGA_BASE; + irq -= OMAP_FPGA_IRQ_BASE; if (irq < 8) __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) @@ -65,7 +65,7 @@ static void fpga_ack_irq(unsigned int irq) static void fpga_unmask_irq(unsigned int irq) { - irq -= OMAP1510_IH_FPGA_BASE; + irq -= OMAP_FPGA_IRQ_BASE; if (irq < 8) __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) | (1 << irq)), @@ -95,8 +95,8 @@ void innovator_fpga_IRQ_demux(unsigned int irq, struct irq_desc *desc) if (!stat) return; - for (fpga_irq = OMAP1510_IH_FPGA_BASE; - (fpga_irq < (OMAP1510_IH_FPGA_BASE + NR_FPGA_IRQS)) && stat; + for (fpga_irq = OMAP_FPGA_IRQ_BASE; + (fpga_irq < OMAP_FPGA_IRQ_END) && stat; fpga_irq++, stat >>= 1) { if (stat & 1) { d = irq_desc + fpga_irq; @@ -151,7 +151,7 @@ void omap1510_fpga_init_irq(void) __raw_writeb(0, OMAP1510_FPGA_IMR_HI); __raw_writeb(0, INNOVATOR_FPGA_IMR2); - for (i = OMAP1510_IH_FPGA_BASE; i < (OMAP1510_IH_FPGA_BASE + NR_FPGA_IRQS); i++) { + for (i = OMAP_FPGA_IRQ_BASE; i < OMAP_FPGA_IRQ_END; i++) { if (i == OMAP1510_INT_FPGA_TS) { /* diff --git a/arch/arm/mach-omap1/leds-osk.c b/arch/arm/mach-omap1/leds-osk.c index 026685e..754383d 100644 --- a/arch/arm/mach-omap1/leds-osk.c +++ b/arch/arm/mach-omap1/leds-osk.c @@ -1,11 +1,9 @@ /* * linux/arch/arm/mach-omap1/leds-osk.c * - * LED driver for OSK, and optionally Mistral QVGA, boards + * LED driver for OSK with optional Mistral QVGA board */ #include <linux/init.h> -#include <linux/workqueue.h> -#include <linux/i2c/tps65010.h> #include <asm/hardware.h> #include <asm/leds.h> @@ -20,49 +18,11 @@ #define LED_STATE_CLAIMED (1 << 1) static u8 led_state; -#define GREEN_LED (1 << 0) /* TPS65010 LED1 */ -#define AMBER_LED (1 << 1) /* TPS65010 LED2 */ -#define RED_LED (1 << 2) /* TPS65010 GPIO2 */ #define TIMER_LED (1 << 3) /* Mistral board */ #define IDLE_LED (1 << 4) /* Mistral board */ static u8 hw_led_state; -/* TPS65010 leds are changed using i2c -- from a task context. - * Using one of these for the "idle" LED would be impractical... - */ -#define TPS_LEDS (GREEN_LED | RED_LED | AMBER_LED) - -static u8 tps_leds_change; - -static void tps_work(struct work_struct *unused) -{ - for (;;) { - u8 leds; - - local_irq_disable(); - leds = tps_leds_change; - tps_leds_change = 0; - local_irq_enable(); - - if (!leds) - break; - - /* careful: the set_led() value is on/off/blink */ - if (leds & GREEN_LED) - tps65010_set_led(LED1, !!(hw_led_state & GREEN_LED)); - if (leds & AMBER_LED) - tps65010_set_led(LED2, !!(hw_led_state & AMBER_LED)); - - /* the gpio led doesn't have that issue */ - if (leds & RED_LED) - tps65010_set_gpio_out_value(GPIO2, - !(hw_led_state & RED_LED)); - } -} - -static DECLARE_WORK(work, tps_work); - #ifdef CONFIG_OMAP_OSK_MISTRAL /* For now, all system indicators require the Mistral board, since that @@ -112,7 +72,6 @@ void osk_leds_event(led_event_t evt) case led_stop: led_state &= ~LED_STATE_ENABLED; hw_led_state = 0; - /* NOTE: work may still be pending!! */ break; case led_claim: @@ -145,48 +104,11 @@ void osk_leds_event(led_event_t evt) #endif /* CONFIG_OMAP_OSK_MISTRAL */ - /* "green" == tps LED1 (leftmost, normally power-good) - * works only with DC adapter, not on battery power! - */ - case led_green_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= GREEN_LED; - break; - case led_green_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~GREEN_LED; - break; - - /* "amber" == tps LED2 (middle) */ - case led_amber_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= AMBER_LED; - break; - case led_amber_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~AMBER_LED; - break; - - /* "red" == LED on tps gpio3 (rightmost) */ - case led_red_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= RED_LED; - break; - case led_red_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~RED_LED; - break; - default: break; } leds ^= hw_led_state; - leds &= TPS_LEDS; - if (leds && (led_state & LED_STATE_CLAIMED)) { - tps_leds_change |= leds; - schedule_work(&work); - } done: local_irq_restore(flags); diff --git a/arch/arm/mach-omap1/mcbsp.c b/arch/arm/mach-omap1/mcbsp.c new file mode 100644 index 0000000..2d2c252 --- /dev/null +++ b/arch/arm/mach-omap1/mcbsp.c @@ -0,0 +1,280 @@ +/* + * linux/arch/arm/mach-omap1/mcbsp.c + * + * Copyright (C) 2008 Instituto Nokia de Tecnologia + * Contact: Eduardo Valentin <eduardo.valentin@indt.org.br> + * + * 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. + * + * Multichannel mode not supported. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#include <asm/arch/dma.h> +#include <asm/arch/mux.h> +#include <asm/arch/cpu.h> +#include <asm/arch/mcbsp.h> +#include <asm/arch/dsp_common.h> + +#define DPS_RSTCT2_PER_EN (1 << 0) +#define DSP_RSTCT2_WD_PER_EN (1 << 1) + +struct mcbsp_internal_clk { + struct clk clk; + struct clk **childs; + int n_childs; +}; + +#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) +static void omap_mcbsp_clk_init(struct mcbsp_internal_clk *mclk) +{ + const char *clk_names[] = { "dsp_ck", "api_ck", "dspxor_ck" }; + int i; + + mclk->n_childs = ARRAY_SIZE(clk_names); + mclk->childs = kzalloc(mclk->n_childs * sizeof(struct clk *), + GFP_KERNEL); + + for (i = 0; i < mclk->n_childs; i++) { + /* We fake a platform device to get correct device id */ + struct platform_device pdev; + + pdev.dev.bus = &platform_bus_type; + pdev.id = mclk->clk.id; + mclk->childs[i] = clk_get(&pdev.dev, clk_names[i]); + if (IS_ERR(mclk->childs[i])) + printk(KERN_ERR "Could not get clock %s (%d).\n", + clk_names[i], mclk->clk.id); + } +} + +static int omap_mcbsp_clk_enable(struct clk *clk) +{ + struct mcbsp_internal_clk *mclk = container_of(clk, + struct mcbsp_internal_clk, clk); + int i; + + for (i = 0; i < mclk->n_childs; i++) + clk_enable(mclk->childs[i]); + return 0; +} + +static void omap_mcbsp_clk_disable(struct clk *clk) +{ + struct mcbsp_internal_clk *mclk = container_of(clk, + struct mcbsp_internal_clk, clk); + int i; + + for (i = 0; i < mclk->n_childs; i++) + clk_disable(mclk->childs[i]); +} + +static struct mcbsp_internal_clk omap_mcbsp_clks[] = { + { + .clk = { + .name = "mcbsp_clk", + .id = 1, + .enable = omap_mcbsp_clk_enable, + .disable = omap_mcbsp_clk_disable, + }, + }, + { + .clk = { + .name = "mcbsp_clk", + .id = 3, + .enable = omap_mcbsp_clk_enable, + .disable = omap_mcbsp_clk_disable, + }, + }, +}; + +#define omap_mcbsp_clks_size ARRAY_SIZE(omap_mcbsp_clks) +#else +#define omap_mcbsp_clks_size 0 +static struct mcbsp_internal_clk __initdata *omap_mcbsp_clks; +static inline void omap_mcbsp_clk_init(struct mcbsp_internal_clk *mclk) +{ } +#endif + +static int omap1_mcbsp_check(unsigned int id) +{ + /* REVISIT: Check correctly for number of registered McBSPs */ + if (cpu_is_omap730()) { + if (id > OMAP_MAX_MCBSP_COUNT - 2) { + printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", + id + 1); + return -ENODEV; + } + return 0; + } + + if (cpu_is_omap15xx() || cpu_is_omap16xx()) { + if (id > OMAP_MAX_MCBSP_COUNT - 1) { + printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", + id + 1); + return -ENODEV; + } + return 0; + } + + return -ENODEV; +} + +static void omap1_mcbsp_request(unsigned int id) +{ + /* + * On 1510, 1610 and 1710, McBSP1 and McBSP3 + * are DSP public peripherals. + */ + if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) { + omap_dsp_request_mem(); + /* + * DSP external peripheral reset + * FIXME: This should be moved to dsp code + */ + __raw_writew(__raw_readw(DSP_RSTCT2) | DPS_RSTCT2_PER_EN | + DSP_RSTCT2_WD_PER_EN, DSP_RSTCT2); + } +} + +static void omap1_mcbsp_free(unsigned int id) +{ + if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) + omap_dsp_release_mem(); +} + +static struct omap_mcbsp_ops omap1_mcbsp_ops = { + .check = omap1_mcbsp_check, + .request = omap1_mcbsp_request, + .free = omap1_mcbsp_free, +}; + +#ifdef CONFIG_ARCH_OMAP730 +static struct omap_mcbsp_platform_data omap730_mcbsp_pdata[] = { + { + .virt_base = io_p2v(OMAP730_MCBSP1_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP_DMA_MCBSP1_TX, + .rx_irq = INT_730_McBSP1RX, + .tx_irq = INT_730_McBSP1TX, + .ops = &omap1_mcbsp_ops, + }, + { + .virt_base = io_p2v(OMAP730_MCBSP2_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP3_RX, + .dma_tx_sync = OMAP_DMA_MCBSP3_TX, + .rx_irq = INT_730_McBSP2RX, + .tx_irq = INT_730_McBSP2TX, + .ops = &omap1_mcbsp_ops, + }, +}; +#define OMAP730_MCBSP_PDATA_SZ ARRAY_SIZE(omap730_mcbsp_pdata) +#else +#define omap730_mcbsp_pdata NULL +#define OMAP730_MCBSP_PDATA_SZ 0 +#endif + +#ifdef CONFIG_ARCH_OMAP15XX +static struct omap_mcbsp_platform_data omap15xx_mcbsp_pdata[] = { + { + .virt_base = OMAP1510_MCBSP1_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP_DMA_MCBSP1_TX, + .rx_irq = INT_McBSP1RX, + .tx_irq = INT_McBSP1TX, + .ops = &omap1_mcbsp_ops, + .clk_name = "mcbsp_clk", + }, + { + .virt_base = io_p2v(OMAP1510_MCBSP2_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP2_RX, + .dma_tx_sync = OMAP_DMA_MCBSP2_TX, + .rx_irq = INT_1510_SPI_RX, + .tx_irq = INT_1510_SPI_TX, + .ops = &omap1_mcbsp_ops, + }, + { + .virt_base = OMAP1510_MCBSP3_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP3_RX, + .dma_tx_sync = OMAP_DMA_MCBSP3_TX, + .rx_irq = INT_McBSP3RX, + .tx_irq = INT_McBSP3TX, + .ops = &omap1_mcbsp_ops, + .clk_name = "mcbsp_clk", + }, +}; +#define OMAP15XX_MCBSP_PDATA_SZ ARRAY_SIZE(omap15xx_mcbsp_pdata) +#else +#define omap15xx_mcbsp_pdata NULL +#define OMAP15XX_MCBSP_PDATA_SZ 0 +#endif + +#ifdef CONFIG_ARCH_OMAP16XX +static struct omap_mcbsp_platform_data omap16xx_mcbsp_pdata[] = { + { + .virt_base = OMAP1610_MCBSP1_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP_DMA_MCBSP1_TX, + .rx_irq = INT_McBSP1RX, + .tx_irq = INT_McBSP1TX, + .ops = &omap1_mcbsp_ops, + .clk_name = "mcbsp_clk", + }, + { + .virt_base = io_p2v(OMAP1610_MCBSP2_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP2_RX, + .dma_tx_sync = OMAP_DMA_MCBSP2_TX, + .rx_irq = INT_1610_McBSP2_RX, + .tx_irq = INT_1610_McBSP2_TX, + .ops = &omap1_mcbsp_ops, + }, + { + .virt_base = OMAP1610_MCBSP3_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP3_RX, + .dma_tx_sync = OMAP_DMA_MCBSP3_TX, + .rx_irq = INT_McBSP3RX, + .tx_irq = INT_McBSP3TX, + .ops = &omap1_mcbsp_ops, + .clk_name = "mcbsp_clk", + }, +}; +#define OMAP16XX_MCBSP_PDATA_SZ ARRAY_SIZE(omap16xx_mcbsp_pdata) +#else +#define omap16xx_mcbsp_pdata NULL +#define OMAP16XX_MCBSP_PDATA_SZ 0 +#endif + +int __init omap1_mcbsp_init(void) +{ + int i; + + for (i = 0; i < omap_mcbsp_clks_size; i++) { + if (cpu_is_omap15xx() || cpu_is_omap16xx()) { + omap_mcbsp_clk_init(&omap_mcbsp_clks[i]); + clk_register(&omap_mcbsp_clks[i].clk); + } + } + + if (cpu_is_omap730()) + omap_mcbsp_register_board_cfg(omap730_mcbsp_pdata, + OMAP730_MCBSP_PDATA_SZ); + + if (cpu_is_omap15xx()) + omap_mcbsp_register_board_cfg(omap15xx_mcbsp_pdata, + OMAP15XX_MCBSP_PDATA_SZ); + + if (cpu_is_omap16xx()) + omap_mcbsp_register_board_cfg(omap16xx_mcbsp_pdata, + OMAP16XX_MCBSP_PDATA_SZ); + + return omap_mcbsp_init(); +} + +arch_initcall(omap1_mcbsp_init); diff --git a/arch/arm/mach-omap1/mux.c b/arch/arm/mach-omap1/mux.c index 52c70e5..e207bf7 100644 --- a/arch/arm/mach-omap1/mux.c +++ b/arch/arm/mach-omap1/mux.c @@ -3,9 +3,9 @@ * * OMAP1 pin multiplexing configurations * - * Copyright (C) 2003 - 2005 Nokia Corporation + * Copyright (C) 2003 - 2008 Nokia Corporation * - * Written by Tony Lindgren <tony.lindgren@nokia.com> + * Written by Tony Lindgren * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,8 +32,10 @@ #ifdef CONFIG_OMAP_MUX +static struct omap_mux_cfg arch_mux_cfg; + #ifdef CONFIG_ARCH_OMAP730 -struct pin_config __initdata_or_module omap730_pins[] = { +static struct pin_config __initdata_or_module omap730_pins[] = { MUX_CFG_730("E2_730_KBR0", 12, 21, 0, 20, 1, 0) MUX_CFG_730("J7_730_KBR1", 12, 25, 0, 24, 1, 0) MUX_CFG_730("E1_730_KBR2", 12, 29, 0, 28, 1, 0) @@ -49,10 +51,14 @@ MUX_CFG_730("AA17_730_USB_DM", 2, 21, 0, 20, 0, 0) MUX_CFG_730("W16_730_USB_PU_EN", 2, 25, 0, 24, 0, 0) MUX_CFG_730("W17_730_USB_VBUSI", 2, 29, 0, 28, 0, 0) }; -#endif +#define OMAP730_PINS_SZ ARRAY_SIZE(omap730_pins) +#else +#define omap730_pins NULL +#define OMAP730_PINS_SZ 0 +#endif /* CONFIG_ARCH_OMAP730 */ #if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) -struct pin_config __initdata_or_module omap1xxx_pins[] = { +static struct pin_config __initdata_or_module omap1xxx_pins[] = { /* * description mux mode mux pull pull pull pu_pd pu dbg * reg offset mode reg bit ena reg @@ -306,22 +312,136 @@ MUX_CFG("Y12_1610_CCP_CLKP", 8, 18, 6, 1, 24, 1, 1, 0, 0) MUX_CFG("W13_1610_CCP_CLKM", 9, 0, 6, 1, 28, 1, 1, 0, 0) MUX_CFG("W14_1610_CCP_DATAP", 9, 24, 6, 2, 4, 1, 2, 0, 0) MUX_CFG("Y14_1610_CCP_DATAM", 9, 21, 6, 2, 3, 1, 2, 0, 0) - }; +#define OMAP1XXX_PINS_SZ ARRAY_SIZE(omap1xxx_pins) +#else +#define omap1xxx_pins NULL +#define OMAP1XXX_PINS_SZ 0 #endif /* CONFIG_ARCH_OMAP15XX || CONFIG_ARCH_OMAP16XX */ -int __init omap1_mux_init(void) +int __init_or_module omap1_cfg_reg(const struct pin_config *cfg) { - -#ifdef CONFIG_ARCH_OMAP730 - omap_mux_register(omap730_pins, ARRAY_SIZE(omap730_pins)); + static DEFINE_SPINLOCK(mux_spin_lock); + unsigned long flags; + unsigned int reg_orig = 0, reg = 0, pu_pd_orig = 0, pu_pd = 0, + pull_orig = 0, pull = 0; + unsigned int mask, warn = 0; + + /* Check the mux register in question */ + if (cfg->mux_reg) { + unsigned tmp1, tmp2; + + spin_lock_irqsave(&mux_spin_lock, flags); + reg_orig = omap_readl(cfg->mux_reg); + + /* The mux registers always seem to be 3 bits long */ + mask = (0x7 << cfg->mask_offset); + tmp1 = reg_orig & mask; + reg = reg_orig & ~mask; + + tmp2 = (cfg->mask << cfg->mask_offset); + reg |= tmp2; + + if (tmp1 != tmp2) + warn = 1; + + omap_writel(reg, cfg->mux_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + + /* Check for pull up or pull down selection on 1610 */ + if (!cpu_is_omap15xx()) { + if (cfg->pu_pd_reg && cfg->pull_val) { + spin_lock_irqsave(&mux_spin_lock, flags); + pu_pd_orig = omap_readl(cfg->pu_pd_reg); + mask = 1 << cfg->pull_bit; + + if (cfg->pu_pd_val) { + if (!(pu_pd_orig & mask)) + warn = 1; + /* Use pull up */ + pu_pd = pu_pd_orig | mask; + } else { + if (pu_pd_orig & mask) + warn = 1; + /* Use pull down */ + pu_pd = pu_pd_orig & ~mask; + } + omap_writel(pu_pd, cfg->pu_pd_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + } + + /* Check for an associated pull down register */ + if (cfg->pull_reg) { + spin_lock_irqsave(&mux_spin_lock, flags); + pull_orig = omap_readl(cfg->pull_reg); + mask = 1 << cfg->pull_bit; + + if (cfg->pull_val) { + if (pull_orig & mask) + warn = 1; + /* Low bit = pull enabled */ + pull = pull_orig & ~mask; + } else { + if (!(pull_orig & mask)) + warn = 1; + /* High bit = pull disabled */ + pull = pull_orig | mask; + } + + omap_writel(pull, cfg->pull_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + + if (warn) { +#ifdef CONFIG_OMAP_MUX_WARNINGS + printk(KERN_WARNING "MUX: initialized %s\n", cfg->name); #endif - -#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) - omap_mux_register(omap1xxx_pins, ARRAY_SIZE(omap1xxx_pins)); + } + +#ifdef CONFIG_OMAP_MUX_DEBUG + if (cfg->debug || warn) { + printk("MUX: Setting register %s\n", cfg->name); + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->mux_reg_name, cfg->mux_reg, reg_orig, reg); + + if (!cpu_is_omap15xx()) { + if (cfg->pu_pd_reg && cfg->pull_val) { + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->pu_pd_name, cfg->pu_pd_reg, + pu_pd_orig, pu_pd); + } + } + + if (cfg->pull_reg) + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->pull_name, cfg->pull_reg, pull_orig, pull); + } #endif +#ifdef CONFIG_OMAP_MUX_ERRORS + return warn ? -ETXTBSY : 0; +#else return 0; +#endif +} + +int __init omap1_mux_init(void) +{ + if (cpu_is_omap730()) { + arch_mux_cfg.pins = omap730_pins; + arch_mux_cfg.size = OMAP730_PINS_SZ; + arch_mux_cfg.cfg_reg = omap1_cfg_reg; + } + + if (cpu_is_omap15xx() || cpu_is_omap16xx()) { + arch_mux_cfg.pins = omap1xxx_pins; + arch_mux_cfg.size = OMAP1XXX_PINS_SZ; + arch_mux_cfg.cfg_reg = omap1_cfg_reg; + } + + return omap_mux_register(&arch_mux_cfg); } #endif diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c index e6c64e1..742f79e 100644 --- a/arch/arm/mach-omap1/pm.c +++ b/arch/arm/mach-omap1/pm.c @@ -116,13 +116,6 @@ void omap_pm_idle(void) return; } - /* - * Since an interrupt may set up a timer, we don't want to - * reprogram the hardware timer with interrupts enabled. - * Re-enable interrupts only after returning from idle. - */ - timer_dyn_reprogram(); - #ifdef CONFIG_OMAP_MPU_TIMER #warning Enable 32kHz OS timer in order to allow sleep states in idle use_idlect1 = use_idlect1 & ~(1 << 9); diff --git a/arch/arm/mach-omap1/sram.S b/arch/arm/mach-omap1/sram.S new file mode 100644 index 0000000..126d252 --- /dev/null +++ b/arch/arm/mach-omap1/sram.S @@ -0,0 +1,57 @@ +/* + * linux/arch/arm/plat-omap/sram-fn.S + * + * Functions that need to be run in internal SRAM + * + * 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. + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/arch/io.h> +#include <asm/hardware.h> + + .text + +/* + * Reprograms ULPD and CKCTL. + */ +ENTRY(omap1_sram_reprogram_clock) + stmfd sp!, {r0 - r12, lr} @ save registers on stack + + mov r2, #IO_ADDRESS(DPLL_CTL) & 0xff000000 + orr r2, r2, #IO_ADDRESS(DPLL_CTL) & 0x00ff0000 + orr r2, r2, #IO_ADDRESS(DPLL_CTL) & 0x0000ff00 + + mov r3, #IO_ADDRESS(ARM_CKCTL) & 0xff000000 + orr r3, r3, #IO_ADDRESS(ARM_CKCTL) & 0x00ff0000 + orr r3, r3, #IO_ADDRESS(ARM_CKCTL) & 0x0000ff00 + + tst r0, #1 << 4 @ want lock mode? + beq newck @ nope + bic r0, r0, #1 << 4 @ else clear lock bit + strh r0, [r2] @ set dpll into bypass mode + orr r0, r0, #1 << 4 @ set lock bit again + +newck: + strh r1, [r3] @ write new ckctl value + strh r0, [r2] @ write new dpll value + + mov r4, #0x0700 @ let the clocks settle + orr r4, r4, #0x00ff +delay: sub r4, r4, #1 + cmp r4, #0 + bne delay + +lock: ldrh r4, [r2], #0 @ read back dpll value + tst r0, #1 << 4 @ want lock mode? + beq out @ nope + tst r4, #1 << 0 @ dpll rate locked? + beq lock @ try again + +out: + ldmfd sp!, {r0 - r12, pc} @ restore regs and return +ENTRY(omap1_sram_reprogram_clock_sz) + .word . - omap1_sram_reprogram_clock diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index a4f8b20..5d2b270 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c @@ -56,37 +56,6 @@ #define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE #define OMAP_MPU_TIMER_OFFSET 0x100 -/* cycles to nsec conversions taken from arch/i386/kernel/timers/timer_tsc.c, - * converted to use kHz by Kevin Hilman */ -/* convert from cycles(64bits) => nanoseconds (64bits) - * basic equation: - * ns = cycles / (freq / ns_per_sec) - * ns = cycles * (ns_per_sec / freq) - * ns = cycles * (10^9 / (cpu_khz * 10^3)) - * ns = cycles * (10^6 / cpu_khz) - * - * Then we use scaling math (suggested by george at mvista.com) to get: - * ns = cycles * (10^6 * SC / cpu_khz / SC - * ns = cycles * cyc2ns_scale / SC - * - * And since SC is a constant power of two, we can convert the div - * into a shift. - * -johnstul at us.ibm.com "math is hard, lets go shopping!" - */ -static unsigned long cyc2ns_scale; -#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ - -static inline void set_cyc2ns_scale(unsigned long cpu_khz) -{ - cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz; -} - -static inline unsigned long long cycles_2_ns(unsigned long long cyc) -{ - return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; -} - - typedef struct { u32 cntl; /* CNTL_TIMER, R/W */ u32 load_tim; /* LOAD_TIM, W */ @@ -194,8 +163,6 @@ static struct irqaction omap_mpu_timer1_irq = { static __init void omap_init_mpu_timer(unsigned long rate) { - set_cyc2ns_scale(rate / 1000); - setup_irq(INT_TIMER1, &omap_mpu_timer1_irq); omap_mpu_timer_start(0, (rate / HZ) - 1, 1); @@ -260,22 +227,6 @@ static void __init omap_init_clocksource(unsigned long rate) printk(err, clocksource_mpu.name); } - -/* - * Scheduler clock - returns current time in nanosec units. - */ -unsigned long long sched_clock(void) -{ - unsigned long ticks = 0 - omap_mpu_timer_read(1); - unsigned long long ticks64; - - ticks64 = omap_mpu_timer2_overflows; - ticks64 <<= 32; - ticks64 |= ticks; - - return cycles_2_ns(ticks64); -} - /* * --------------------------------------------------------------------------- * Timer initialization diff --git a/arch/arm/mach-omap1/timer32k.c b/arch/arm/mach-omap1/timer32k.c new file mode 100644 index 0000000..fbbdb80 --- /dev/null +++ b/arch/arm/mach-omap1/timer32k.c @@ -0,0 +1,209 @@ +/* + * linux/arch/arm/mach-omap1/timer32k.c + * + * OMAP 32K Timer + * + * Copyright (C) 2004 - 2005 Nokia Corporation + * Partial timer rewrite and additional dynamic tick timer support by + * Tony Lindgen <tony@atomide.com> and + * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * OMAP Dual-mode timer framework support by Timo Teras + * + * MPU timer code based on the older MPU timer code for OMAP + * Copyright (C) 2000 RidgeRun, Inc. + * Author: Greg Lonnon <glonnon@ridgerun.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> + +#include <asm/system.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/leds.h> +#include <asm/irq.h> +#include <asm/mach/irq.h> +#include <asm/mach/time.h> +#include <asm/arch/dmtimer.h> + +struct sys_timer omap_timer; + +/* + * --------------------------------------------------------------------------- + * 32KHz OS timer + * + * This currently works only on 16xx, as 1510 does not have the continuous + * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track + * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer + * on 1510 would be possible, but the timer would not be as accurate as + * with the 32KHz synchronized timer. + * --------------------------------------------------------------------------- + */ + +#if defined(CONFIG_ARCH_OMAP16XX) +#define TIMER_32K_SYNCHRONIZED 0xfffbc410 +#else +#error OMAP 32KHz timer does not currently work on 15XX! +#endif + +/* 16xx specific defines */ +#define OMAP1_32K_TIMER_BASE 0xfffb9000 +#define OMAP1_32K_TIMER_CR 0x08 +#define OMAP1_32K_TIMER_TVR 0x00 +#define OMAP1_32K_TIMER_TCR 0x04 + +#define OMAP_32K_TICKS_PER_SEC (32768) + +/* + * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 + * so with HZ = 128, TVR = 255. + */ +#define OMAP_32K_TIMER_TICK_PERIOD ((OMAP_32K_TICKS_PER_SEC / HZ) - 1) + +#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ + (((nr_jiffies) * (clock_rate)) / HZ) + +static inline void omap_32k_timer_write(int val, int reg) +{ + omap_writew(val, OMAP1_32K_TIMER_BASE + reg); +} + +static inline unsigned long omap_32k_timer_read(int reg) +{ + return omap_readl(OMAP1_32K_TIMER_BASE + reg) & 0xffffff; +} + +static inline void omap_32k_timer_start(unsigned long load_val) +{ + if (!load_val) + load_val = 1; + omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR); + omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR); +} + +static inline void omap_32k_timer_stop(void) +{ + omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR); +} + +#define omap_32k_timer_ack_irq() + +static int omap_32k_timer_set_next_event(unsigned long delta, + struct clock_event_device *dev) +{ + omap_32k_timer_start(delta); + + return 0; +} + +static void omap_32k_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + omap_32k_timer_stop(); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); + break; + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + break; + case CLOCK_EVT_MODE_RESUME: + break; + } +} + +static struct clock_event_device clockevent_32k_timer = { + .name = "32k-timer", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + .set_next_event = omap_32k_timer_set_next_event, + .set_mode = omap_32k_timer_set_mode, +}; + +/* + * The 32KHz synchronized timer is an additional timer on 16xx. + * It is always running. + */ +static inline unsigned long omap_32k_sync_timer_read(void) +{ + return omap_readl(TIMER_32K_SYNCHRONIZED); +} + +static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = &clockevent_32k_timer; + omap_32k_timer_ack_irq(); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction omap_32k_timer_irq = { + .name = "32KHz timer", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = omap_32k_timer_interrupt, +}; + +static __init void omap_init_32k_timer(void) +{ + setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); + + clockevent_32k_timer.mult = div_sc(OMAP_32K_TICKS_PER_SEC, + NSEC_PER_SEC, + clockevent_32k_timer.shift); + clockevent_32k_timer.max_delta_ns = + clockevent_delta2ns(0xfffffffe, &clockevent_32k_timer); + clockevent_32k_timer.min_delta_ns = + clockevent_delta2ns(1, &clockevent_32k_timer); + + clockevent_32k_timer.cpumask = cpumask_of_cpu(0); + clockevents_register_device(&clockevent_32k_timer); +} + +/* + * --------------------------------------------------------------------------- + * Timer initialization + * --------------------------------------------------------------------------- + */ +static void __init omap_timer_init(void) +{ +#ifdef CONFIG_OMAP_DM_TIMER + omap_dm_timer_init(); +#endif + omap_init_32k_timer(); +} + +struct sys_timer omap_timer = { + .init = omap_timer_init, +}; |