[ARM] 5477/1: Freescale STMP platform support [6/10]
Sources: common STMP3xxx platform support Signed-off-by: dmitry pervushin <dpervushin@embeddedalley.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
e317872ac5
commit
5cccd37ea1
37
arch/arm/plat-stmp3xxx/Kconfig
Normal file
37
arch/arm/plat-stmp3xxx/Kconfig
Normal file
@ -0,0 +1,37 @@
|
||||
if ARCH_STMP3XXX
|
||||
|
||||
menu "Freescale STMP3xxx implementations"
|
||||
|
||||
choice
|
||||
prompt "Select STMP3xxx chip family"
|
||||
|
||||
config ARCH_STMP37XX
|
||||
bool "Freescale SMTP37xx"
|
||||
select CPU_ARM926T
|
||||
---help---
|
||||
STMP37xx refers to 3700 through 3769 chips
|
||||
|
||||
config ARCH_STMP378X
|
||||
bool "Freescale STMP378x"
|
||||
select CPU_ARM926T
|
||||
---help---
|
||||
STMP378x refers to 3780 through 3789 chips
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Select STMP3xxx board type"
|
||||
|
||||
config MACH_STMP37XX
|
||||
depends on ARCH_STMP37XX
|
||||
bool "Freescale STMP37xx development board"
|
||||
|
||||
config MACH_STMP378X
|
||||
depends on ARCH_STMP378X
|
||||
bool "Freescale STMP378x development board"
|
||||
|
||||
endchoice
|
||||
|
||||
endmenu
|
||||
|
||||
endif
|
5
arch/arm/plat-stmp3xxx/Makefile
Normal file
5
arch/arm/plat-stmp3xxx/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
# Object file lists.
|
||||
obj-y += core.o timer.o irq.o dma.o clock.o pinmux.o
|
1112
arch/arm/plat-stmp3xxx/clock.c
Normal file
1112
arch/arm/plat-stmp3xxx/clock.c
Normal file
File diff suppressed because it is too large
Load Diff
61
arch/arm/plat-stmp3xxx/clock.h
Normal file
61
arch/arm/plat-stmp3xxx/clock.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Clock control driver for Freescale STMP37XX/STMP378X - internal header file
|
||||
*
|
||||
* Author: Vitaly Wool <vital@embeddedalley.com>
|
||||
*
|
||||
* Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
#ifndef __ARCH_ARM_STMX3XXX_CLOCK_H__
|
||||
#define __ARCH_ARM_STMX3XXX_CLOCK_H__
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
struct clk_ops {
|
||||
int (*enable) (struct clk *);
|
||||
int (*disable) (struct clk *);
|
||||
long (*get_rate) (struct clk *);
|
||||
long (*round_rate) (struct clk *, u32);
|
||||
int (*set_rate) (struct clk *, u32);
|
||||
int (*set_parent) (struct clk *, struct clk *);
|
||||
};
|
||||
|
||||
struct clk {
|
||||
struct clk *parent;
|
||||
u32 rate;
|
||||
u32 flags;
|
||||
u8 scale_shift;
|
||||
u8 enable_shift;
|
||||
u8 bypass_shift;
|
||||
u8 busy_bit;
|
||||
s8 usage;
|
||||
int enable_wait;
|
||||
int enable_negate;
|
||||
u32 saved_div;
|
||||
void __iomem *enable_reg;
|
||||
void __iomem *scale_reg;
|
||||
void __iomem *bypass_reg;
|
||||
void __iomem *busy_reg;
|
||||
struct clk_ops *ops;
|
||||
};
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
|
||||
/* Flags */
|
||||
#define RATE_PROPAGATES (1<<0)
|
||||
#define NEEDS_INITIALIZATION (1<<1)
|
||||
#define PARENT_SET_RATE (1<<2)
|
||||
#define FIXED_RATE (1<<3)
|
||||
#define ENABLED (1<<4)
|
||||
#define NEEDS_SET_PARENT (1<<5)
|
||||
|
||||
#endif
|
127
arch/arm/plat-stmp3xxx/core.c
Normal file
127
arch/arm/plat-stmp3xxx/core.c
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Freescale STMP37XX/STMP378X core routines
|
||||
*
|
||||
* Embedded Alley Solutions, Inc <source@embeddedalley.com>
|
||||
*
|
||||
* Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <mach/stmp3xxx.h>
|
||||
#include <mach/dma.h>
|
||||
#include <mach/regs-clkctrl.h>
|
||||
|
||||
static int __stmp3xxx_reset_block(void __iomem *hwreg, int just_enable)
|
||||
{
|
||||
u32 c;
|
||||
int timeout;
|
||||
|
||||
/* the process of software reset of IP block is done
|
||||
in several steps:
|
||||
|
||||
- clear SFTRST and wait for block is enabled;
|
||||
- clear clock gating (CLKGATE bit);
|
||||
- set the SFTRST again and wait for block is in reset;
|
||||
- clear SFTRST and wait for reset completion.
|
||||
*/
|
||||
c = __raw_readl(hwreg);
|
||||
c &= ~(1<<31); /* clear SFTRST */
|
||||
__raw_writel(c, hwreg);
|
||||
for (timeout = 1000000; timeout > 0; timeout--)
|
||||
/* still in SFTRST state ? */
|
||||
if ((__raw_readl(hwreg) & (1<<31)) == 0)
|
||||
break;
|
||||
if (timeout <= 0) {
|
||||
printk(KERN_ERR"%s(%p): timeout when enabling\n",
|
||||
__func__, hwreg);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
c = __raw_readl(hwreg);
|
||||
c &= ~(1<<30); /* clear CLKGATE */
|
||||
__raw_writel(c, hwreg);
|
||||
|
||||
if (!just_enable) {
|
||||
c = __raw_readl(hwreg);
|
||||
c |= (1<<31); /* now again set SFTRST */
|
||||
__raw_writel(c, hwreg);
|
||||
for (timeout = 1000000; timeout > 0; timeout--)
|
||||
/* poll until CLKGATE set */
|
||||
if (__raw_readl(hwreg) & (1<<30))
|
||||
break;
|
||||
if (timeout <= 0) {
|
||||
printk(KERN_ERR"%s(%p): timeout when resetting\n",
|
||||
__func__, hwreg);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
c = __raw_readl(hwreg);
|
||||
c &= ~(1<<31); /* clear SFTRST */
|
||||
__raw_writel(c, hwreg);
|
||||
for (timeout = 1000000; timeout > 0; timeout--)
|
||||
/* still in SFTRST state ? */
|
||||
if ((__raw_readl(hwreg) & (1<<31)) == 0)
|
||||
break;
|
||||
if (timeout <= 0) {
|
||||
printk(KERN_ERR"%s(%p): timeout when enabling "
|
||||
"after reset\n", __func__, hwreg);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
c = __raw_readl(hwreg);
|
||||
c &= ~(1<<30); /* clear CLKGATE */
|
||||
__raw_writel(c, hwreg);
|
||||
}
|
||||
for (timeout = 1000000; timeout > 0; timeout--)
|
||||
/* still in SFTRST state ? */
|
||||
if ((__raw_readl(hwreg) & (1<<30)) == 0)
|
||||
break;
|
||||
|
||||
if (timeout <= 0) {
|
||||
printk(KERN_ERR"%s(%p): timeout when unclockgating\n",
|
||||
__func__, hwreg);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stmp3xxx_reset_block(void __iomem *hwreg, int just_enable)
|
||||
{
|
||||
int try = 10;
|
||||
int r;
|
||||
|
||||
while (try--) {
|
||||
r = __stmp3xxx_reset_block(hwreg, just_enable);
|
||||
if (!r)
|
||||
break;
|
||||
pr_debug("%s: try %d failed\n", __func__, 10 - try);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_reset_block);
|
||||
|
||||
struct platform_device stmp3xxx_dbguart = {
|
||||
.name = "stmp3xxx-dbguart",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
void __init stmp3xxx_init(void)
|
||||
{
|
||||
/* Turn off auto-slow and other tricks */
|
||||
HW_CLKCTRL_HBUS_CLR(0x07f00000U);
|
||||
|
||||
stmp3xxx_dma_init();
|
||||
}
|
462
arch/arm/plat-stmp3xxx/dma.c
Normal file
462
arch/arm/plat-stmp3xxx/dma.c
Normal file
@ -0,0 +1,462 @@
|
||||
/*
|
||||
* DMA helper routines for Freescale STMP37XX/STMP378X
|
||||
*
|
||||
* Author: dmitry pervushin <dpervushin@embeddedalley.com>
|
||||
*
|
||||
* Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/sysdev.h>
|
||||
#include <linux/cpufreq.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <mach/regs-apbx.h>
|
||||
#include <mach/regs-apbh.h>
|
||||
|
||||
static const size_t pool_item_size = sizeof(struct stmp3xxx_dma_command);
|
||||
static const size_t pool_alignment = 8;
|
||||
static struct stmp3xxx_dma_user {
|
||||
void *pool;
|
||||
int inuse;
|
||||
const char *name;
|
||||
} channels[MAX_DMA_CHANNELS];
|
||||
|
||||
static inline int dmach(int ch)
|
||||
{
|
||||
return ch % 16;
|
||||
}
|
||||
|
||||
static inline int dmabus(int ch)
|
||||
{
|
||||
return ch / 16;
|
||||
}
|
||||
|
||||
#define IS_VALID_CHANNEL(ch) ((ch) >= 0 && (ch) < MAX_DMA_CHANNELS)
|
||||
#define IS_USED(ch) (channels[ch].inuse)
|
||||
|
||||
int stmp3xxx_dma_request(int ch, struct device *dev, const char *name)
|
||||
{
|
||||
struct stmp3xxx_dma_user *user;
|
||||
int err = 0;
|
||||
|
||||
user = channels + ch;
|
||||
if (!IS_VALID_CHANNEL(ch)) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (IS_USED(ch)) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
/* Create a pool to allocate dma commands from */
|
||||
user->pool = dma_pool_create(name, dev, pool_item_size,
|
||||
pool_alignment, PAGE_SIZE);
|
||||
if (user->pool == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
user->name = name;
|
||||
user->inuse++;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_dma_request);
|
||||
|
||||
int stmp3xxx_dma_release(int ch)
|
||||
{
|
||||
struct stmp3xxx_dma_user *user = channels + ch;
|
||||
int err = 0;
|
||||
|
||||
if (!IS_VALID_CHANNEL(ch)) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (!IS_USED(ch)) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
BUG_ON(user->pool == NULL);
|
||||
dma_pool_destroy(user->pool);
|
||||
user->inuse--;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_dma_release);
|
||||
|
||||
int stmp3xxx_dma_read_semaphore(int channel)
|
||||
{
|
||||
int sem = -1;
|
||||
|
||||
switch (dmabus(channel)) {
|
||||
case STMP3XXX_BUS_APBH:
|
||||
sem =
|
||||
(HW_APBH_CHn_SEMA_RD(dmach(channel)) &
|
||||
BM_APBH_CHn_SEMA_PHORE) >> BP_APBH_CHn_SEMA_PHORE;
|
||||
break;
|
||||
|
||||
case STMP3XXX_BUS_APBX:
|
||||
sem =
|
||||
(HW_APBX_CHn_SEMA_RD(dmach(channel)) &
|
||||
BM_APBX_CHn_SEMA_PHORE) >> BP_APBX_CHn_SEMA_PHORE;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return sem;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_dma_read_semaphore);
|
||||
|
||||
int stmp3xxx_dma_allocate_command(int channel,
|
||||
struct stmp3xxx_dma_descriptor *descriptor)
|
||||
{
|
||||
struct stmp3xxx_dma_user *user = channels + channel;
|
||||
int err = 0;
|
||||
|
||||
if (!IS_VALID_CHANNEL(channel)) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (!IS_USED(channel)) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
if (descriptor == NULL) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Allocate memory for a command from the buffer */
|
||||
descriptor->command =
|
||||
dma_pool_alloc(user->pool, GFP_KERNEL, &descriptor->handle);
|
||||
|
||||
/* Check it worked */
|
||||
if (!descriptor->command) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(descriptor->command, 0, pool_item_size);
|
||||
out:
|
||||
WARN_ON(err);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_dma_allocate_command);
|
||||
|
||||
int stmp3xxx_dma_free_command(int channel,
|
||||
struct stmp3xxx_dma_descriptor *descriptor)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!IS_VALID_CHANNEL(channel)) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (!IS_USED(channel)) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Return the command memory to the pool */
|
||||
dma_pool_free(channels[channel].pool, descriptor->command,
|
||||
descriptor->handle);
|
||||
|
||||
/* Initialise descriptor so we're not tempted to use it */
|
||||
descriptor->command = NULL;
|
||||
descriptor->handle = 0;
|
||||
descriptor->virtual_buf_ptr = NULL;
|
||||
descriptor->next_descr = NULL;
|
||||
|
||||
WARN_ON(err);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_dma_free_command);
|
||||
|
||||
void stmp3xxx_dma_go(int channel,
|
||||
struct stmp3xxx_dma_descriptor *head, u32 semaphore)
|
||||
{
|
||||
int ch = dmach(channel);
|
||||
|
||||
switch (dmabus(channel)) {
|
||||
case STMP3XXX_BUS_APBH:
|
||||
/* Set next command */
|
||||
HW_APBH_CHn_NXTCMDAR_WR(ch, head->handle);
|
||||
/* Set counting semaphore (kicks off transfer). Assumes
|
||||
peripheral has been set up correctly */
|
||||
HW_APBH_CHn_SEMA_WR(ch, semaphore);
|
||||
break;
|
||||
|
||||
case STMP3XXX_BUS_APBX:
|
||||
/* Set next command */
|
||||
HW_APBX_CHn_NXTCMDAR_WR(ch, head->handle);
|
||||
/* Set counting semaphore (kicks off transfer). Assumes
|
||||
peripheral has been set up correctly */
|
||||
HW_APBX_CHn_SEMA_WR(ch, semaphore);
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_dma_go);
|
||||
|
||||
int stmp3xxx_dma_running(int channel)
|
||||
{
|
||||
switch (dmabus(channel)) {
|
||||
case STMP3XXX_BUS_APBH:
|
||||
return HW_APBH_CHn_SEMA_RD(dmach(channel)) &
|
||||
BM_APBH_CHn_SEMA_PHORE;
|
||||
|
||||
case STMP3XXX_BUS_APBX:
|
||||
return HW_APBX_CHn_SEMA_RD(dmach(channel)) &
|
||||
BM_APBX_CHn_SEMA_PHORE;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_dma_running);
|
||||
|
||||
/*
|
||||
* Circular dma chain management
|
||||
*/
|
||||
void stmp3xxx_dma_free_chain(struct stmp37xx_circ_dma_chain *chain)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chain->total_count; i++)
|
||||
stmp3xxx_dma_free_command(
|
||||
STMP3xxx_DMA(chain->channel, chain->bus),
|
||||
&chain->chain[i]);
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_dma_free_chain);
|
||||
|
||||
int stmp3xxx_dma_make_chain(int ch, struct stmp37xx_circ_dma_chain *chain,
|
||||
struct stmp3xxx_dma_descriptor descriptors[],
|
||||
unsigned items)
|
||||
{
|
||||
int i;
|
||||
int err = 0;
|
||||
|
||||
if (items == 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < items; i++) {
|
||||
err = stmp3xxx_dma_allocate_command(ch, &descriptors[i]);
|
||||
if (err) {
|
||||
WARN_ON(err);
|
||||
/*
|
||||
* Couldn't allocate the whole chain.
|
||||
* deallocate what has been allocated
|
||||
*/
|
||||
if (i) {
|
||||
do {
|
||||
stmp3xxx_dma_free_command(ch,
|
||||
&descriptors
|
||||
[i]);
|
||||
} while (i-- >= 0);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* link them! */
|
||||
if (i > 0) {
|
||||
descriptors[i - 1].next_descr = &descriptors[i];
|
||||
descriptors[i - 1].command->next =
|
||||
descriptors[i].handle;
|
||||
}
|
||||
}
|
||||
|
||||
/* make list circular */
|
||||
descriptors[items - 1].next_descr = &descriptors[0];
|
||||
descriptors[items - 1].command->next = descriptors[0].handle;
|
||||
|
||||
chain->total_count = items;
|
||||
chain->chain = descriptors;
|
||||
chain->free_index = 0;
|
||||
chain->active_index = 0;
|
||||
chain->cooked_index = 0;
|
||||
chain->free_count = items;
|
||||
chain->active_count = 0;
|
||||
chain->cooked_count = 0;
|
||||
chain->bus = dmabus(ch);
|
||||
chain->channel = dmach(ch);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_dma_make_chain);
|
||||
|
||||
void stmp37xx_circ_clear_chain(struct stmp37xx_circ_dma_chain *chain)
|
||||
{
|
||||
BUG_ON(stmp3xxx_dma_running(STMP3xxx_DMA(chain->channel, chain->bus)) >
|
||||
0);
|
||||
chain->free_index = 0;
|
||||
chain->active_index = 0;
|
||||
chain->cooked_index = 0;
|
||||
chain->free_count = chain->total_count;
|
||||
chain->active_count = 0;
|
||||
chain->cooked_count = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp37xx_circ_clear_chain);
|
||||
|
||||
void stmp37xx_circ_advance_free(struct stmp37xx_circ_dma_chain *chain,
|
||||
unsigned count)
|
||||
{
|
||||
BUG_ON(chain->cooked_count < count);
|
||||
|
||||
chain->cooked_count -= count;
|
||||
chain->cooked_index += count;
|
||||
chain->cooked_index %= chain->total_count;
|
||||
chain->free_count += count;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp37xx_circ_advance_free);
|
||||
|
||||
void stmp37xx_circ_advance_active(struct stmp37xx_circ_dma_chain *chain,
|
||||
unsigned count)
|
||||
{
|
||||
BUG_ON(chain->free_count < count);
|
||||
|
||||
chain->free_count -= count;
|
||||
chain->free_index += count;
|
||||
chain->free_index %= chain->total_count;
|
||||
chain->active_count += count;
|
||||
|
||||
switch (chain->bus) {
|
||||
case STMP3XXX_BUS_APBH:
|
||||
/* Set counting semaphore (kicks off transfer). Assumes
|
||||
peripheral has been set up correctly */
|
||||
HW_APBH_CHn_SEMA_CLR(chain->channel,
|
||||
BM_APBH_CHn_SEMA_INCREMENT_SEMA);
|
||||
HW_APBH_CHn_SEMA_SET(chain->channel,
|
||||
BF_APBH_CHn_SEMA_INCREMENT_SEMA(count));
|
||||
break;
|
||||
|
||||
case STMP3XXX_BUS_APBX:
|
||||
/* Set counting semaphore (kicks off transfer). Assumes
|
||||
peripheral has been set up correctly */
|
||||
HW_APBX_CHn_SEMA_CLR(chain->channel,
|
||||
BM_APBX_CHn_SEMA_INCREMENT_SEMA);
|
||||
HW_APBX_CHn_SEMA_SET(chain->channel,
|
||||
BF_APBX_CHn_SEMA_INCREMENT_SEMA(count));
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(stmp37xx_circ_advance_active);
|
||||
|
||||
unsigned stmp37xx_circ_advance_cooked(struct stmp37xx_circ_dma_chain *chain)
|
||||
{
|
||||
unsigned cooked;
|
||||
|
||||
cooked = chain->active_count -
|
||||
stmp3xxx_dma_read_semaphore(STMP3xxx_DMA(chain->channel, chain->bus));
|
||||
|
||||
chain->active_count -= cooked;
|
||||
chain->active_index += cooked;
|
||||
chain->active_index %= chain->total_count;
|
||||
|
||||
chain->cooked_count += cooked;
|
||||
|
||||
return cooked;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp37xx_circ_advance_cooked);
|
||||
|
||||
void stmp3xxx_dma_set_alt_target(int channel, int function)
|
||||
{
|
||||
#if defined(CONFIG_ARCH_STMP37XX)
|
||||
unsigned bits = 4;
|
||||
#elif defined(CONFIG_ARCH_STMP378X)
|
||||
unsigned bits = 2;
|
||||
#else
|
||||
#error wrong arch
|
||||
#endif
|
||||
int shift = dmach(channel) * bits;
|
||||
unsigned mask = (1<<bits) - 1;
|
||||
|
||||
BUG_ON(function < 0 || function >= (1<<bits));
|
||||
pr_debug("%s: channel = %d, using mask %x, "
|
||||
"shift = %d\n", __func__, channel, mask, shift);
|
||||
|
||||
switch (dmabus(channel)) {
|
||||
case STMP3XXX_BUS_APBH:
|
||||
HW_APBH_DEVSEL_CLR(mask<<shift);
|
||||
HW_APBH_DEVSEL_SET(function<<shift);
|
||||
break;
|
||||
case STMP3XXX_BUS_APBX:
|
||||
HW_APBX_DEVSEL_CLR(mask<<shift);
|
||||
HW_APBX_DEVSEL_SET(function<<shift);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_dma_set_alt_target);
|
||||
|
||||
void stmp3xxx_dma_suspend(void)
|
||||
{
|
||||
HW_APBH_CTRL0_SET(BM_APBH_CTRL0_CLKGATE);
|
||||
HW_APBX_CTRL0_SET(BM_APBX_CTRL0_CLKGATE);
|
||||
}
|
||||
|
||||
void stmp3xxx_dma_resume(void)
|
||||
{
|
||||
HW_APBH_CTRL0_CLR(BM_APBH_CTRL0_CLKGATE | BM_APBH_CTRL0_SFTRST);
|
||||
HW_APBX_CTRL0_CLR(BM_APBX_CTRL0_CLKGATE | BM_APBX_CTRL0_SFTRST);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
|
||||
struct dma_notifier_block {
|
||||
struct notifier_block nb;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static int dma_cpufreq_notifier(struct notifier_block *self,
|
||||
unsigned long phase, void *p)
|
||||
{
|
||||
switch (phase) {
|
||||
case CPUFREQ_POSTCHANGE:
|
||||
stmp3xxx_dma_resume();
|
||||
break;
|
||||
|
||||
case CPUFREQ_PRECHANGE:
|
||||
stmp3xxx_dma_suspend();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct dma_notifier_block dma_cpufreq_nb = {
|
||||
.nb = {
|
||||
.notifier_call = dma_cpufreq_notifier,
|
||||
},
|
||||
};
|
||||
#endif /* CONFIG_CPU_FREQ */
|
||||
|
||||
void __init stmp3xxx_dma_init(void)
|
||||
{
|
||||
HW_APBH_CTRL0_CLR(BM_APBH_CTRL0_CLKGATE | BM_APBH_CTRL0_SFTRST);
|
||||
HW_APBX_CTRL0_CLR(BM_APBX_CTRL0_CLKGATE | BM_APBX_CTRL0_SFTRST);
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
cpufreq_register_notifier(&dma_cpufreq_nb.nb,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
#endif /* CONFIG_CPU_FREQ */
|
||||
|
||||
}
|
59
arch/arm/plat-stmp3xxx/irq.c
Normal file
59
arch/arm/plat-stmp3xxx/irq.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Freescale STMP37XX/STMP378X common interrupt handling code
|
||||
*
|
||||
* Author: Vladislav Buzov <vbuzov@embeddedalley.com>
|
||||
*
|
||||
* Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/sysdev.h>
|
||||
|
||||
#include <mach/stmp3xxx.h>
|
||||
#include <mach/regs-icoll.h>
|
||||
|
||||
void __init stmp3xxx_init_irq(struct irq_chip *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* Reset the interrupt controller */
|
||||
HW_ICOLL_CTRL_CLR(BM_ICOLL_CTRL_CLKGATE);
|
||||
udelay(10);
|
||||
HW_ICOLL_CTRL_CLR(BM_ICOLL_CTRL_SFTRST);
|
||||
udelay(10);
|
||||
HW_ICOLL_CTRL_SET(BM_ICOLL_CTRL_SFTRST);
|
||||
while (!(HW_ICOLL_CTRL_RD() & BM_ICOLL_CTRL_CLKGATE))
|
||||
continue;
|
||||
HW_ICOLL_CTRL_CLR(BM_ICOLL_CTRL_SFTRST | BM_ICOLL_CTRL_CLKGATE);
|
||||
|
||||
/* Disable all interrupts initially */
|
||||
for (i = 0; i < NR_REAL_IRQS; i++) {
|
||||
chip->mask(i);
|
||||
set_irq_chip(i, chip);
|
||||
set_irq_handler(i, handle_level_irq);
|
||||
set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
|
||||
}
|
||||
|
||||
/* Ensure vector is cleared */
|
||||
HW_ICOLL_LEVELACK_WR(1);
|
||||
HW_ICOLL_LEVELACK_WR(2);
|
||||
HW_ICOLL_LEVELACK_WR(4);
|
||||
HW_ICOLL_LEVELACK_WR(8);
|
||||
|
||||
HW_ICOLL_VECTOR_WR(0);
|
||||
/* Barrier */
|
||||
(void) HW_ICOLL_STAT_RD();
|
||||
}
|
||||
|
545
arch/arm/plat-stmp3xxx/pinmux.c
Normal file
545
arch/arm/plat-stmp3xxx/pinmux.c
Normal file
@ -0,0 +1,545 @@
|
||||
/*
|
||||
* Freescale STMP378X/STMP378X Pin Multiplexing
|
||||
*
|
||||
* Author: Vladislav Buzov <vbuzov@embeddedalley.com>
|
||||
*
|
||||
* Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sysdev.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/sysdev.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/regs-pinctrl.h>
|
||||
#include <mach/pins.h>
|
||||
#include <mach/pinmux.h>
|
||||
|
||||
#define NR_BANKS ARRAY_SIZE(pinmux_banks)
|
||||
static struct stmp3xxx_pinmux_bank pinmux_banks[] = {
|
||||
[0] = {
|
||||
.hw_muxsel = {
|
||||
HW_PINCTRL_MUXSEL0_ADDR,
|
||||
HW_PINCTRL_MUXSEL1_ADDR
|
||||
},
|
||||
.hw_drive = {
|
||||
HW_PINCTRL_DRIVE0_ADDR,
|
||||
HW_PINCTRL_DRIVE1_ADDR,
|
||||
HW_PINCTRL_DRIVE2_ADDR,
|
||||
HW_PINCTRL_DRIVE3_ADDR
|
||||
},
|
||||
.hw_pull = HW_PINCTRL_PULL0_ADDR,
|
||||
.functions = { 0x0, 0x1, 0x2, 0x3 },
|
||||
.strengths = { 0x0, 0x1, 0x2, 0x3, 0xff },
|
||||
|
||||
.hw_gpio_read = HW_PINCTRL_DIN0_ADDR,
|
||||
.hw_gpio_set = HW_PINCTRL_DOUT0_ADDR + HW_STMP3xxx_SET,
|
||||
.hw_gpio_clr = HW_PINCTRL_DOUT0_ADDR + HW_STMP3xxx_CLR,
|
||||
.hw_gpio_doe = HW_PINCTRL_DOE0_ADDR,
|
||||
.irq = IRQ_GPIO0,
|
||||
|
||||
.pin2irq = HW_PINCTRL_PIN2IRQ0_ADDR,
|
||||
.irqstat = HW_PINCTRL_IRQSTAT0_ADDR,
|
||||
.irqlevel = HW_PINCTRL_IRQLEVEL0_ADDR,
|
||||
.irqpolarity = HW_PINCTRL_IRQPOL0_ADDR,
|
||||
.irqen = HW_PINCTRL_IRQEN0_ADDR,
|
||||
},
|
||||
[1] = {
|
||||
.hw_muxsel = {
|
||||
HW_PINCTRL_MUXSEL2_ADDR,
|
||||
HW_PINCTRL_MUXSEL3_ADDR
|
||||
},
|
||||
.hw_drive = {
|
||||
HW_PINCTRL_DRIVE4_ADDR,
|
||||
HW_PINCTRL_DRIVE5_ADDR,
|
||||
HW_PINCTRL_DRIVE6_ADDR,
|
||||
HW_PINCTRL_DRIVE7_ADDR
|
||||
},
|
||||
.hw_pull = HW_PINCTRL_PULL1_ADDR,
|
||||
.functions = { 0x0, 0x1, 0x2, 0x3 },
|
||||
.strengths = { 0x0, 0x1, 0x2, 0x3, 0xff },
|
||||
|
||||
.hw_gpio_read = HW_PINCTRL_DIN1_ADDR,
|
||||
.hw_gpio_set = HW_PINCTRL_DOUT1_ADDR + HW_STMP3xxx_SET,
|
||||
.hw_gpio_clr = HW_PINCTRL_DOUT1_ADDR + HW_STMP3xxx_CLR,
|
||||
.hw_gpio_doe = HW_PINCTRL_DOE1_ADDR,
|
||||
.irq = IRQ_GPIO1,
|
||||
|
||||
.pin2irq = HW_PINCTRL_PIN2IRQ1_ADDR,
|
||||
.irqstat = HW_PINCTRL_IRQSTAT1_ADDR,
|
||||
.irqlevel = HW_PINCTRL_IRQLEVEL1_ADDR,
|
||||
.irqpolarity = HW_PINCTRL_IRQPOL1_ADDR,
|
||||
.irqen = HW_PINCTRL_IRQEN1_ADDR,
|
||||
},
|
||||
[2] = {
|
||||
.hw_muxsel = {
|
||||
HW_PINCTRL_MUXSEL4_ADDR,
|
||||
HW_PINCTRL_MUXSEL5_ADDR,
|
||||
},
|
||||
.hw_drive = {
|
||||
HW_PINCTRL_DRIVE8_ADDR,
|
||||
HW_PINCTRL_DRIVE9_ADDR,
|
||||
HW_PINCTRL_DRIVE10_ADDR,
|
||||
HW_PINCTRL_DRIVE11_ADDR,
|
||||
},
|
||||
.hw_pull = HW_PINCTRL_PULL2_ADDR,
|
||||
.functions = { 0x0, 0x1, 0x2, 0x3 },
|
||||
.strengths = { 0x0, 0x1, 0x2, 0x1, 0x2 },
|
||||
|
||||
.hw_gpio_read = HW_PINCTRL_DIN2_ADDR,
|
||||
.hw_gpio_set = HW_PINCTRL_DOUT2_ADDR + HW_STMP3xxx_SET,
|
||||
.hw_gpio_clr = HW_PINCTRL_DOUT2_ADDR + HW_STMP3xxx_CLR,
|
||||
.hw_gpio_doe = HW_PINCTRL_DOE2_ADDR,
|
||||
.irq = IRQ_GPIO2,
|
||||
|
||||
.pin2irq = HW_PINCTRL_PIN2IRQ2_ADDR,
|
||||
.irqstat = HW_PINCTRL_IRQSTAT2_ADDR,
|
||||
.irqlevel = HW_PINCTRL_IRQLEVEL2_ADDR,
|
||||
.irqpolarity = HW_PINCTRL_IRQPOL2_ADDR,
|
||||
.irqen = HW_PINCTRL_IRQEN2_ADDR,
|
||||
},
|
||||
[3] = {
|
||||
.hw_muxsel = {
|
||||
HW_PINCTRL_MUXSEL6_ADDR,
|
||||
HW_PINCTRL_MUXSEL7_ADDR,
|
||||
},
|
||||
.hw_drive = {
|
||||
HW_PINCTRL_DRIVE12_ADDR,
|
||||
HW_PINCTRL_DRIVE13_ADDR,
|
||||
HW_PINCTRL_DRIVE14_ADDR,
|
||||
NULL,
|
||||
},
|
||||
.hw_pull = HW_PINCTRL_PULL3_ADDR,
|
||||
.functions = {0x0, 0x1, 0x2, 0x3},
|
||||
.strengths = {0x0, 0x1, 0x2, 0x3, 0xff},
|
||||
},
|
||||
};
|
||||
|
||||
static inline struct stmp3xxx_pinmux_bank *
|
||||
stmp3xxx_pinmux_bank(unsigned id, unsigned *bank, unsigned *pin)
|
||||
{
|
||||
unsigned b, p;
|
||||
|
||||
b = STMP3XXX_PINID_TO_BANK(id);
|
||||
p = STMP3XXX_PINID_TO_PINNUM(id);
|
||||
BUG_ON(b >= NR_BANKS);
|
||||
if (bank)
|
||||
*bank = b;
|
||||
if (pin)
|
||||
*pin = p;
|
||||
return &pinmux_banks[b];
|
||||
}
|
||||
|
||||
/* Check if requested pin is owned by caller */
|
||||
static int stmp3xxx_check_pin(unsigned id, const char *label)
|
||||
{
|
||||
unsigned pin;
|
||||
struct stmp3xxx_pinmux_bank *pm = stmp3xxx_pinmux_bank(id, NULL, &pin);
|
||||
|
||||
if (!test_bit(pin, &pm->pin_map)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Accessing free pin %x, caller %s\n",
|
||||
__func__, id, label);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (label && pm->pin_labels[pin] &&
|
||||
strcmp(label, pm->pin_labels[pin])) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Wrong pin owner %x, caller %s owner %s\n",
|
||||
__func__, id, label, pm->pin_labels[pin]);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stmp3xxx_pin_strength(unsigned id, enum pin_strength strength,
|
||||
const char *label)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pbank;
|
||||
void __iomem *hwdrive;
|
||||
u32 shift, val;
|
||||
u32 bank, pin;
|
||||
|
||||
pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
|
||||
pr_debug("%s: label %s bank %d pin %d strength %d\n", __func__, label,
|
||||
bank, pin, strength);
|
||||
|
||||
hwdrive = pbank->hw_drive[pin / HW_DRIVE_PIN_NUM];
|
||||
shift = (pin % HW_DRIVE_PIN_NUM) * HW_DRIVE_PIN_LEN;
|
||||
val = pbank->strengths[strength];
|
||||
if (val == 0xff) {
|
||||
printk(KERN_WARNING
|
||||
"%s: strength is not supported for bank %d, caller %s",
|
||||
__func__, bank, label);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stmp3xxx_check_pin(id, label))
|
||||
return;
|
||||
|
||||
pr_debug("%s: writing 0x%x to 0x%p register\n", __func__,
|
||||
val << shift, hwdrive);
|
||||
__raw_writel(HW_DRIVE_PINDRV_MASK << shift, hwdrive + HW_STMP3xxx_CLR);
|
||||
__raw_writel(val << shift, hwdrive + HW_STMP3xxx_SET);
|
||||
}
|
||||
|
||||
void stmp3xxx_pin_voltage(unsigned id, enum pin_voltage voltage,
|
||||
const char *label)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pbank;
|
||||
void __iomem *hwdrive;
|
||||
u32 shift;
|
||||
u32 bank, pin;
|
||||
|
||||
pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
|
||||
pr_debug("%s: label %s bank %d pin %d voltage %d\n", __func__, label,
|
||||
bank, pin, voltage);
|
||||
|
||||
hwdrive = pbank->hw_drive[pin / HW_DRIVE_PIN_NUM];
|
||||
shift = (pin % HW_DRIVE_PIN_NUM) * HW_DRIVE_PIN_LEN;
|
||||
|
||||
if (stmp3xxx_check_pin(id, label))
|
||||
return;
|
||||
|
||||
pr_debug("%s: changing 0x%x bit in 0x%p register\n",
|
||||
__func__, HW_DRIVE_PINV_MASK << shift, hwdrive);
|
||||
if (voltage == PIN_1_8V)
|
||||
__raw_writel(HW_DRIVE_PINV_MASK << shift,
|
||||
hwdrive + HW_STMP3xxx_CLR);
|
||||
else
|
||||
__raw_writel(HW_DRIVE_PINV_MASK << shift,
|
||||
hwdrive + HW_STMP3xxx_SET);
|
||||
}
|
||||
|
||||
void stmp3xxx_pin_pullup(unsigned id, int enable, const char *label)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pbank;
|
||||
void __iomem *hwpull;
|
||||
u32 bank, pin;
|
||||
|
||||
pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
|
||||
pr_debug("%s: label %s bank %d pin %d enable %d\n", __func__, label,
|
||||
bank, pin, enable);
|
||||
|
||||
hwpull = pbank->hw_pull;
|
||||
|
||||
if (stmp3xxx_check_pin(id, label))
|
||||
return;
|
||||
|
||||
pr_debug("%s: changing 0x%x bit in 0x%p register\n",
|
||||
__func__, 1 << pin, hwpull);
|
||||
__raw_writel(1 << pin,
|
||||
hwpull + (enable ? HW_STMP3xxx_SET : HW_STMP3xxx_CLR));
|
||||
}
|
||||
|
||||
int stmp3xxx_request_pin(unsigned id, enum pin_fun fun, const char *label)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pbank;
|
||||
u32 bank, pin;
|
||||
int ret = 0;
|
||||
|
||||
pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
|
||||
pr_debug("%s: label %s bank %d pin %d fun %d\n", __func__, label,
|
||||
bank, pin, fun);
|
||||
|
||||
if (test_bit(pin, &pbank->pin_map)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: CONFLICT DETECTED pin %d:%d caller %s owner %s\n",
|
||||
__func__, bank, pin, label, pbank->pin_labels[pin]);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
set_bit(pin, &pbank->pin_map);
|
||||
pbank->pin_labels[pin] = label;
|
||||
|
||||
stmp3xxx_set_pin_type(id, fun);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void stmp3xxx_set_pin_type(unsigned id, enum pin_fun fun)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pbank;
|
||||
void __iomem *hwmux;
|
||||
u32 shift, val;
|
||||
u32 bank, pin;
|
||||
|
||||
pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
|
||||
|
||||
hwmux = pbank->hw_muxsel[pin / HW_MUXSEL_PIN_NUM];
|
||||
shift = (pin % HW_MUXSEL_PIN_NUM) * HW_MUXSEL_PIN_LEN;
|
||||
|
||||
val = pbank->functions[fun];
|
||||
shift = (pin % HW_MUXSEL_PIN_NUM) * HW_MUXSEL_PIN_LEN;
|
||||
pr_debug("%s: writing 0x%x to 0x%p register\n",
|
||||
__func__, val << shift, hwmux);
|
||||
__raw_writel(HW_MUXSEL_PINFUN_MASK << shift, hwmux + HW_STMP3xxx_CLR);
|
||||
__raw_writel(val << shift, hwmux + HW_STMP3xxx_SET);
|
||||
}
|
||||
|
||||
void stmp3xxx_release_pin(unsigned id, const char *label)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pbank;
|
||||
u32 bank, pin;
|
||||
|
||||
pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
|
||||
pr_debug("%s: label %s bank %d pin %d\n", __func__, label, bank, pin);
|
||||
|
||||
if (stmp3xxx_check_pin(id, label))
|
||||
return;
|
||||
|
||||
clear_bit(pin, &pbank->pin_map);
|
||||
pbank->pin_labels[pin] = NULL;
|
||||
}
|
||||
|
||||
int stmp3xxx_request_pin_group(struct pin_group *pin_group, const char *label)
|
||||
{
|
||||
struct pin_desc *pin;
|
||||
int p;
|
||||
int err = 0;
|
||||
|
||||
/* Allocate and configure pins */
|
||||
for (p = 0; p < pin_group->nr_pins; p++) {
|
||||
pr_debug("%s: #%d\n", __func__, p);
|
||||
pin = &pin_group->pins[p];
|
||||
|
||||
err = stmp3xxx_request_pin(pin->id, pin->fun, label);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
stmp3xxx_pin_strength(pin->id, pin->strength, label);
|
||||
stmp3xxx_pin_voltage(pin->id, pin->voltage, label);
|
||||
stmp3xxx_pin_pullup(pin->id, pin->pullup, label);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
/* Release allocated pins in case of error */
|
||||
while (--p >= 0) {
|
||||
pr_debug("%s: releasing #%d\n", __func__, p);
|
||||
stmp3xxx_release_pin(pin_group->pins[p].id, label);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_request_pin_group);
|
||||
|
||||
void stmp3xxx_release_pin_group(struct pin_group *pin_group, const char *label)
|
||||
{
|
||||
struct pin_desc *pin;
|
||||
int p;
|
||||
|
||||
for (p = 0; p < pin_group->nr_pins; p++) {
|
||||
pin = &pin_group->pins[p];
|
||||
stmp3xxx_release_pin(pin->id, label);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(stmp3xxx_release_pin_group);
|
||||
|
||||
static int stmp3xxx_irq_to_gpio(int irq,
|
||||
struct stmp3xxx_pinmux_bank **bank, unsigned *gpio)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pm;
|
||||
|
||||
for (pm = pinmux_banks; pm < pinmux_banks + NR_BANKS; pm++)
|
||||
if (pm->virq <= irq && irq < pm->virq + 32) {
|
||||
*bank = pm;
|
||||
*gpio = irq - pm->virq;
|
||||
return 0;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int stmp3xxx_set_irqtype(unsigned irq, unsigned type)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pm;
|
||||
unsigned gpio;
|
||||
int l, p;
|
||||
|
||||
stmp3xxx_irq_to_gpio(irq, &pm, &gpio);
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
l = 0; p = 1; break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
l = 0; p = 0; break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
l = 1; p = 1; break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
l = 1; p = 0; break;
|
||||
default:
|
||||
pr_debug("%s: Incorrect GPIO interrupt type 0x%x\n",
|
||||
__func__, type);
|
||||
return -ENXIO;
|
||||
}
|
||||
__raw_writel(1 << gpio,
|
||||
pm->irqlevel + (l ? HW_STMP3xxx_SET : HW_STMP3xxx_CLR));
|
||||
__raw_writel(1 << gpio,
|
||||
pm->irqpolarity + (p ? HW_STMP3xxx_SET : HW_STMP3xxx_CLR));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stmp3xxx_pin_ack_irq(unsigned irq)
|
||||
{
|
||||
u32 stat;
|
||||
struct stmp3xxx_pinmux_bank *pm;
|
||||
unsigned gpio;
|
||||
|
||||
stmp3xxx_irq_to_gpio(irq, &pm, &gpio);
|
||||
stat = __raw_readl(pm->irqstat) & (1<<gpio);
|
||||
__raw_writel(stat, pm->irqstat + HW_STMP3xxx_CLR);
|
||||
}
|
||||
|
||||
static void stmp3xxx_pin_mask_irq(unsigned irq)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pm;
|
||||
unsigned gpio;
|
||||
|
||||
stmp3xxx_irq_to_gpio(irq, &pm, &gpio);
|
||||
__raw_writel(1 << gpio, pm->irqen + HW_STMP3xxx_CLR);
|
||||
__raw_writel(1 << gpio, pm->pin2irq + HW_STMP3xxx_CLR);
|
||||
}
|
||||
|
||||
static void stmp3xxx_pin_unmask_irq(unsigned irq)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pm;
|
||||
unsigned gpio;
|
||||
|
||||
stmp3xxx_irq_to_gpio(irq, &pm, &gpio);
|
||||
__raw_writel(1 << gpio, pm->irqen + HW_STMP3xxx_SET);
|
||||
__raw_writel(1 << gpio, pm->pin2irq + HW_STMP3xxx_SET);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct stmp3xxx_pinmux_bank *to_pinmux_bank(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct stmp3xxx_pinmux_bank, chip);
|
||||
}
|
||||
|
||||
static int stmp3xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pm = to_pinmux_bank(chip);
|
||||
return pm->virq + offset;
|
||||
}
|
||||
|
||||
static int stmp3xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pm = to_pinmux_bank(chip);
|
||||
unsigned v;
|
||||
|
||||
v = __raw_readl(pm->hw_gpio_read) & (1 << offset);
|
||||
return v ? 1 : 0;
|
||||
}
|
||||
|
||||
static void stmp3xxx_gpio_set(struct gpio_chip *chip, unsigned offset, int v)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pm = to_pinmux_bank(chip);
|
||||
|
||||
__raw_writel(1 << offset, v ? pm->hw_gpio_set : pm->hw_gpio_clr);
|
||||
}
|
||||
|
||||
static int stmp3xxx_gpio_output(struct gpio_chip *chip, unsigned offset, int v)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pm = to_pinmux_bank(chip);
|
||||
|
||||
__raw_writel(1 << offset, pm->hw_gpio_doe + HW_STMP3xxx_SET);
|
||||
stmp3xxx_gpio_set(chip, offset, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stmp3xxx_gpio_input(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pm = to_pinmux_bank(chip);
|
||||
|
||||
__raw_writel(1 << offset, pm->hw_gpio_doe + HW_STMP3xxx_CLR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stmp3xxx_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
return stmp3xxx_request_pin(chip->base + offset, PIN_GPIO, "gpio");
|
||||
}
|
||||
|
||||
static void stmp3xxx_gpio_free(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
stmp3xxx_release_pin(chip->base + offset, "gpio");
|
||||
}
|
||||
|
||||
static void stmp3xxx_gpio_irq(u32 irq, struct irq_desc *desc)
|
||||
{
|
||||
struct stmp3xxx_pinmux_bank *pm = get_irq_data(irq);
|
||||
int gpio_irq = pm->virq;
|
||||
u32 stat = __raw_readl(pm->irqstat);
|
||||
|
||||
while (stat) {
|
||||
if (stat & 1)
|
||||
irq_desc[gpio_irq].handle_irq(gpio_irq,
|
||||
&irq_desc[gpio_irq]);
|
||||
gpio_irq++;
|
||||
stat >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static struct irq_chip gpio_irq_chip = {
|
||||
.ack = stmp3xxx_pin_ack_irq,
|
||||
.mask = stmp3xxx_pin_mask_irq,
|
||||
.unmask = stmp3xxx_pin_unmask_irq,
|
||||
.set_type = stmp3xxx_set_irqtype,
|
||||
};
|
||||
|
||||
int __init stmp3xxx_pinmux_init(int virtual_irq_start)
|
||||
{
|
||||
int b, r = 0;
|
||||
struct stmp3xxx_pinmux_bank *pm;
|
||||
int virq;
|
||||
|
||||
for (b = 0; b < 3; b++) {
|
||||
/* only banks 0,1,2 are allowed to GPIO */
|
||||
pm = pinmux_banks + b;
|
||||
pm->chip.base = 32 * b;
|
||||
pm->chip.ngpio = 32;
|
||||
pm->chip.owner = THIS_MODULE;
|
||||
pm->chip.can_sleep = 1;
|
||||
pm->chip.exported = 1;
|
||||
pm->chip.to_irq = stmp3xxx_gpio_to_irq;
|
||||
pm->chip.direction_input = stmp3xxx_gpio_input;
|
||||
pm->chip.direction_output = stmp3xxx_gpio_output;
|
||||
pm->chip.get = stmp3xxx_gpio_get;
|
||||
pm->chip.set = stmp3xxx_gpio_set;
|
||||
pm->chip.request = stmp3xxx_gpio_request;
|
||||
pm->chip.free = stmp3xxx_gpio_free;
|
||||
pm->virq = virtual_irq_start + b * 32;
|
||||
|
||||
for (virq = pm->virq; virq < pm->virq; virq++) {
|
||||
gpio_irq_chip.mask(virq);
|
||||
set_irq_chip(virq, &gpio_irq_chip);
|
||||
set_irq_handler(virq, handle_level_irq);
|
||||
set_irq_flags(virq, IRQF_VALID);
|
||||
}
|
||||
r = gpiochip_add(&pm->chip);
|
||||
if (r < 0)
|
||||
break;
|
||||
set_irq_chained_handler(pm->irq, stmp3xxx_gpio_irq);
|
||||
set_irq_data(pm->irq, pm);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Vladislav Buzov");
|
||||
MODULE_LICENSE("GPL");
|
172
arch/arm/plat-stmp3xxx/timer.c
Normal file
172
arch/arm/plat-stmp3xxx/timer.c
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* System timer for Freescale STMP37XX/STMP378X
|
||||
*
|
||||
* Embedded Alley Solutions, Inc <source@embeddedalley.com>
|
||||
*
|
||||
* Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <asm/mach/time.h>
|
||||
#include <mach/stmp3xxx.h>
|
||||
#include <mach/regs-timrot.h>
|
||||
|
||||
static irqreturn_t
|
||||
stmp3xxx_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *c = dev_id;
|
||||
|
||||
if (HW_TIMROT_TIMCTRLn_RD(0) & (1<<15)) {
|
||||
HW_TIMROT_TIMCTRLn_CLR(0, (1<<15));
|
||||
c->event_handler(c);
|
||||
} else if (HW_TIMROT_TIMCTRLn_RD(1) & (1<<15)) {
|
||||
HW_TIMROT_TIMCTRLn_CLR(1, (1<<15));
|
||||
HW_TIMROT_TIMCTRLn_CLR(1, BM_TIMROT_TIMCTRLn_IRQ_EN);
|
||||
HW_TIMROT_TIMCOUNTn_WR(1, 0xFFFF);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static cycle_t stmp3xxx_clock_read(void)
|
||||
{
|
||||
return ~((HW_TIMROT_TIMCOUNTn_RD(1) & 0xFFFF0000) >> 16);
|
||||
}
|
||||
|
||||
static int
|
||||
stmp3xxx_timrot_set_next_event(unsigned long delta,
|
||||
struct clock_event_device *dev)
|
||||
{
|
||||
HW_TIMROT_TIMCOUNTn_WR(0, delta); /* reload */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
stmp3xxx_timrot_set_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static struct clock_event_device ckevt_timrot = {
|
||||
.name = "timrot",
|
||||
.features = CLOCK_EVT_FEAT_ONESHOT,
|
||||
.shift = 32,
|
||||
.set_next_event = stmp3xxx_timrot_set_next_event,
|
||||
.set_mode = stmp3xxx_timrot_set_mode,
|
||||
};
|
||||
|
||||
static struct clocksource cksrc_stmp3xxx = {
|
||||
.name = "cksrc_stmp3xxx",
|
||||
.rating = 250,
|
||||
.read = stmp3xxx_clock_read,
|
||||
.mask = CLOCKSOURCE_MASK(16),
|
||||
.shift = 10,
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
};
|
||||
|
||||
static struct irqaction stmp3xxx_timer_irq = {
|
||||
.name = "stmp3xxx_timer",
|
||||
.flags = IRQF_DISABLED | IRQF_TIMER,
|
||||
.handler = stmp3xxx_timer_interrupt,
|
||||
.dev_id = &ckevt_timrot,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Set up timer interrupt, and return the current time in seconds.
|
||||
*/
|
||||
static void __init stmp3xxx_init_timer(void)
|
||||
{
|
||||
cksrc_stmp3xxx.mult = clocksource_hz2mult(CLOCK_TICK_RATE,
|
||||
cksrc_stmp3xxx.shift);
|
||||
ckevt_timrot.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC,
|
||||
ckevt_timrot.shift);
|
||||
ckevt_timrot.min_delta_ns = clockevent_delta2ns(2, &ckevt_timrot);
|
||||
ckevt_timrot.max_delta_ns = clockevent_delta2ns(0xFFF, &ckevt_timrot);
|
||||
ckevt_timrot.cpumask = cpumask_of(0);
|
||||
|
||||
HW_TIMROT_ROTCTRL_CLR(BM_TIMROT_ROTCTRL_SFTRST |
|
||||
BM_TIMROT_ROTCTRL_CLKGATE);
|
||||
HW_TIMROT_TIMCOUNTn_WR(0, 0);
|
||||
HW_TIMROT_TIMCOUNTn_WR(1, 0);
|
||||
|
||||
HW_TIMROT_TIMCTRLn_WR(0,
|
||||
(BF_TIMROT_TIMCTRLn_SELECT(8) | /* 32 kHz */
|
||||
BF_TIMROT_TIMCTRLn_PRESCALE(0) |
|
||||
BM_TIMROT_TIMCTRLn_RELOAD |
|
||||
BM_TIMROT_TIMCTRLn_UPDATE |
|
||||
BM_TIMROT_TIMCTRLn_IRQ_EN));
|
||||
HW_TIMROT_TIMCTRLn_WR(1,
|
||||
(BF_TIMROT_TIMCTRLn_SELECT(8) | /* 32 kHz */
|
||||
BF_TIMROT_TIMCTRLn_PRESCALE(0) |
|
||||
BM_TIMROT_TIMCTRLn_RELOAD |
|
||||
BM_TIMROT_TIMCTRLn_UPDATE));
|
||||
|
||||
HW_TIMROT_TIMCOUNTn_WR(0, CLOCK_TICK_RATE / HZ - 1);
|
||||
HW_TIMROT_TIMCOUNTn_WR(1, 0xFFFF); /* reload */
|
||||
|
||||
setup_irq(IRQ_TIMER0, &stmp3xxx_timer_irq);
|
||||
|
||||
clocksource_register(&cksrc_stmp3xxx);
|
||||
clockevents_register_device(&ckevt_timrot);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
void stmp3xxx_suspend_timer(void)
|
||||
{
|
||||
HW_TIMROT_TIMCTRLn_CLR(0, BM_TIMROT_TIMCTRLn_IRQ_EN);
|
||||
HW_TIMROT_TIMCTRLn_CLR(0, (1<<15));
|
||||
HW_TIMROT_ROTCTRL_SET(BM_TIMROT_ROTCTRL_CLKGATE);
|
||||
}
|
||||
|
||||
void stmp3xxx_resume_timer(void)
|
||||
{
|
||||
HW_TIMROT_ROTCTRL_CLR(BM_TIMROT_ROTCTRL_SFTRST |
|
||||
BM_TIMROT_ROTCTRL_CLKGATE);
|
||||
|
||||
|
||||
HW_TIMROT_TIMCTRLn_WR(0,
|
||||
(BF_TIMROT_TIMCTRLn_SELECT(8) | /* 32 kHz */
|
||||
BF_TIMROT_TIMCTRLn_PRESCALE(0) |
|
||||
BM_TIMROT_TIMCTRLn_UPDATE |
|
||||
BM_TIMROT_TIMCTRLn_IRQ_EN));
|
||||
HW_TIMROT_TIMCTRLn_WR(1,
|
||||
(BF_TIMROT_TIMCTRLn_SELECT(8) | /* 32 kHz */
|
||||
BF_TIMROT_TIMCTRLn_PRESCALE(0) |
|
||||
BM_TIMROT_TIMCTRLn_RELOAD |
|
||||
BM_TIMROT_TIMCTRLn_UPDATE));
|
||||
|
||||
HW_TIMROT_TIMCOUNTn_WR(0, CLOCK_TICK_RATE / HZ - 1);
|
||||
HW_TIMROT_TIMCOUNTn_WR(1, 0xFFFF); /* reload */
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define stmp3xxx_suspend_timer NULL
|
||||
#define stmp3xxx_resume_timer NULL
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
struct sys_timer stmp3xxx_timer = {
|
||||
.init = stmp3xxx_init_timer,
|
||||
.suspend = stmp3xxx_suspend_timer,
|
||||
.resume = stmp3xxx_resume_timer,
|
||||
};
|
Loading…
Reference in New Issue
Block a user