Skip to content

Commit

Permalink
Merge tag 'omap-devel-gpmc-fixed-for-v3.7' of git://git.kernel.org/pu…
Browse files Browse the repository at this point in the history
…b/scm/linux/kernel/git/tmlind/linux-omap into next/cleanup

From Tony Lindgren:

Changes for GPMC (General Purpose Memory Controller) that take it
closer for being just a regular device driver.

* tag 'omap-devel-gpmc-fixed-for-v3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
  mtd: nand: omap2: use gpmc provided irqs
  ARM: OMAP2+: gpmc-nand: Modify Interrupt handling
  ARM: OMAP2+: gpmc: Modify interrupt handling
  mtd: onenand: omap2: obtain memory from resource
  mtd: nand: omap2: obtain memory from resource
  ARM: OMAP2+: gpmc-onenand: provide memory as resource
  ARM: OMAP2+: gpmc-nand: update resource with memory
  mtd: nand: omap2: handle nand on gpmc
  ARM: OMAP2+: gpmc-nand: update gpmc-nand regs
  ARM: OMAP2+: gpmc: update nand register helper
  • Loading branch information
Olof Johansson committed Sep 17, 2012
2 parents afdeecc + 5c46845 commit 5ec8d8c
Show file tree
Hide file tree
Showing 7 changed files with 445 additions and 113 deletions.
28 changes: 23 additions & 5 deletions arch/arm/mach-omap2/gpmc-nand.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,23 @@
#include <plat/board.h>
#include <plat/gpmc.h>

static struct resource gpmc_nand_resource = {
.flags = IORESOURCE_MEM,
static struct resource gpmc_nand_resource[] = {
{
.flags = IORESOURCE_MEM,
},
{
.flags = IORESOURCE_IRQ,
},
{
.flags = IORESOURCE_IRQ,
},
};

static struct platform_device gpmc_nand_device = {
.name = "omap2-nand",
.id = 0,
.num_resources = 1,
.resource = &gpmc_nand_resource,
.num_resources = ARRAY_SIZE(gpmc_nand_resource),
.resource = gpmc_nand_resource,
};

static int omap2_nand_gpmc_retime(struct omap_nand_platform_data *gpmc_nand_data)
Expand Down Expand Up @@ -75,6 +83,7 @@ static int omap2_nand_gpmc_retime(struct omap_nand_platform_data *gpmc_nand_data
gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_DEV_SIZE, 0);
gpmc_cs_configure(gpmc_nand_data->cs,
GPMC_CONFIG_DEV_TYPE, GPMC_DEVICETYPE_NAND);
gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_WP, 0);
err = gpmc_cs_set_timings(gpmc_nand_data->cs, &t);
if (err)
return err;
Expand All @@ -90,12 +99,19 @@ int __init gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data)
gpmc_nand_device.dev.platform_data = gpmc_nand_data;

err = gpmc_cs_request(gpmc_nand_data->cs, NAND_IO_SIZE,
&gpmc_nand_data->phys_base);
(unsigned long *)&gpmc_nand_resource[0].start);
if (err < 0) {
dev_err(dev, "Cannot request GPMC CS\n");
return err;
}

gpmc_nand_resource[0].end = gpmc_nand_resource[0].start +
NAND_IO_SIZE - 1;

gpmc_nand_resource[1].start =
gpmc_get_client_irq(GPMC_IRQ_FIFOEVENTENABLE);
gpmc_nand_resource[2].start =
gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT);
/* Set timings in GPMC */
err = omap2_nand_gpmc_retime(gpmc_nand_data);
if (err < 0) {
Expand All @@ -108,6 +124,8 @@ int __init gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data)
gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_RDY_BSY, 1);
}

gpmc_update_nand_reg(&gpmc_nand_data->reg, gpmc_nand_data->cs);

err = platform_device_register(&gpmc_nand_device);
if (err < 0) {
dev_err(dev, "Unable to register NAND device\n");
Expand Down
23 changes: 22 additions & 1 deletion arch/arm/mach-omap2/gpmc-onenand.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,19 @@
#include <plat/board.h>
#include <plat/gpmc.h>

#define ONENAND_IO_SIZE SZ_128K

static struct omap_onenand_platform_data *gpmc_onenand_data;

static struct resource gpmc_onenand_resource = {
.flags = IORESOURCE_MEM,
};

static struct platform_device gpmc_onenand_device = {
.name = "omap2-onenand",
.id = -1,
.num_resources = 1,
.resource = &gpmc_onenand_resource,
};

static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base)
Expand Down Expand Up @@ -390,6 +398,8 @@ static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr)

void __init gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data)
{
int err;

gpmc_onenand_data = _onenand_data;
gpmc_onenand_data->onenand_setup = gpmc_onenand_setup;
gpmc_onenand_device.dev.platform_data = gpmc_onenand_data;
Expand All @@ -401,8 +411,19 @@ void __init gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data)
gpmc_onenand_data->flags |= ONENAND_SYNC_READ;
}

err = gpmc_cs_request(gpmc_onenand_data->cs, ONENAND_IO_SIZE,
(unsigned long *)&gpmc_onenand_resource.start);
if (err < 0) {
pr_err("%s: Cannot request GPMC CS\n", __func__);
return;
}

gpmc_onenand_resource.end = gpmc_onenand_resource.start +
ONENAND_IO_SIZE - 1;

if (platform_device_register(&gpmc_onenand_device) < 0) {
printk(KERN_ERR "Unable to register OneNAND device\n");
pr_err("%s: Unable to register OneNAND device\n", __func__);
gpmc_cs_free(gpmc_onenand_data->cs);
return;
}
}
156 changes: 139 additions & 17 deletions arch/arm/mach-omap2/gpmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@
#define ENABLE_PREFETCH (0x1 << 7)
#define DMA_MPU_MODE 2

/* XXX: Only NAND irq has been considered,currently these are the only ones used
*/
#define GPMC_NR_IRQ 2

struct gpmc_client_irq {
unsigned irq;
u32 bitmask;
};

/* Structure to save gpmc cs context */
struct gpmc_cs_config {
u32 config1;
Expand Down Expand Up @@ -105,6 +114,10 @@ struct omap3_gpmc_regs {
struct gpmc_cs_config cs_context[GPMC_CS_NUM];
};

static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
static struct irq_chip gpmc_irq_chip;
static unsigned gpmc_irq_start;

static struct resource gpmc_mem_root;
static struct resource gpmc_cs_mem[GPMC_CS_NUM];
static DEFINE_SPINLOCK(gpmc_mem_lock);
Expand Down Expand Up @@ -682,6 +695,117 @@ int gpmc_prefetch_reset(int cs)
}
EXPORT_SYMBOL(gpmc_prefetch_reset);

void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
{
reg->gpmc_status = gpmc_base + GPMC_STATUS;
reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET +
GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs;
reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET +
GPMC_CS_NAND_ADDRESS + GPMC_CS_SIZE * cs;
reg->gpmc_nand_data = gpmc_base + GPMC_CS0_OFFSET +
GPMC_CS_NAND_DATA + GPMC_CS_SIZE * cs;
reg->gpmc_prefetch_config1 = gpmc_base + GPMC_PREFETCH_CONFIG1;
reg->gpmc_prefetch_config2 = gpmc_base + GPMC_PREFETCH_CONFIG2;
reg->gpmc_prefetch_control = gpmc_base + GPMC_PREFETCH_CONTROL;
reg->gpmc_prefetch_status = gpmc_base + GPMC_PREFETCH_STATUS;
reg->gpmc_ecc_config = gpmc_base + GPMC_ECC_CONFIG;
reg->gpmc_ecc_control = gpmc_base + GPMC_ECC_CONTROL;
reg->gpmc_ecc_size_config = gpmc_base + GPMC_ECC_SIZE_CONFIG;
reg->gpmc_ecc1_result = gpmc_base + GPMC_ECC1_RESULT;
reg->gpmc_bch_result0 = gpmc_base + GPMC_ECC_BCH_RESULT_0;
}

int gpmc_get_client_irq(unsigned irq_config)
{
int i;

if (hweight32(irq_config) > 1)
return 0;

for (i = 0; i < GPMC_NR_IRQ; i++)
if (gpmc_client_irq[i].bitmask & irq_config)
return gpmc_client_irq[i].irq;

return 0;
}

static int gpmc_irq_endis(unsigned irq, bool endis)
{
int i;
u32 regval;

for (i = 0; i < GPMC_NR_IRQ; i++)
if (irq == gpmc_client_irq[i].irq) {
regval = gpmc_read_reg(GPMC_IRQENABLE);
if (endis)
regval |= gpmc_client_irq[i].bitmask;
else
regval &= ~gpmc_client_irq[i].bitmask;
gpmc_write_reg(GPMC_IRQENABLE, regval);
break;
}

return 0;
}

static void gpmc_irq_disable(struct irq_data *p)
{
gpmc_irq_endis(p->irq, false);
}

static void gpmc_irq_enable(struct irq_data *p)
{
gpmc_irq_endis(p->irq, true);
}

static void gpmc_irq_noop(struct irq_data *data) { }

static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; }

static int gpmc_setup_irq(int gpmc_irq)
{
int i;
u32 regval;

if (!gpmc_irq)
return -EINVAL;

gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0);
if (IS_ERR_VALUE(gpmc_irq_start)) {
pr_err("irq_alloc_descs failed\n");
return gpmc_irq_start;
}

gpmc_irq_chip.name = "gpmc";
gpmc_irq_chip.irq_startup = gpmc_irq_noop_ret;
gpmc_irq_chip.irq_enable = gpmc_irq_enable;
gpmc_irq_chip.irq_disable = gpmc_irq_disable;
gpmc_irq_chip.irq_shutdown = gpmc_irq_noop;
gpmc_irq_chip.irq_ack = gpmc_irq_noop;
gpmc_irq_chip.irq_mask = gpmc_irq_noop;
gpmc_irq_chip.irq_unmask = gpmc_irq_noop;

gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE;
gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT;

for (i = 0; i < GPMC_NR_IRQ; i++) {
gpmc_client_irq[i].irq = gpmc_irq_start + i;
irq_set_chip_and_handler(gpmc_client_irq[i].irq,
&gpmc_irq_chip, handle_simple_irq);
set_irq_flags(gpmc_client_irq[i].irq,
IRQF_VALID | IRQF_NOAUTOEN);
}

/* Disable interrupts */
gpmc_write_reg(GPMC_IRQENABLE, 0);

/* clear interrupts */
regval = gpmc_read_reg(GPMC_IRQSTATUS);
gpmc_write_reg(GPMC_IRQSTATUS, regval);

return request_irq(gpmc_irq, gpmc_handle_irq, 0, "gpmc", NULL);
}

static void __init gpmc_mem_init(void)
{
int cs;
Expand Down Expand Up @@ -711,8 +835,8 @@ static void __init gpmc_mem_init(void)

static int __init gpmc_init(void)
{
u32 l, irq;
int cs, ret = -EINVAL;
u32 l;
int ret = -EINVAL;
int gpmc_irq;
char *ck = NULL;

Expand Down Expand Up @@ -761,16 +885,7 @@ static int __init gpmc_init(void)
gpmc_write_reg(GPMC_SYSCONFIG, l);
gpmc_mem_init();

/* initalize the irq_chained */
irq = OMAP_GPMC_IRQ_BASE;
for (cs = 0; cs < GPMC_CS_NUM; cs++) {
irq_set_chip_and_handler(irq, &dummy_irq_chip,
handle_simple_irq);
set_irq_flags(irq, IRQF_VALID);
irq++;
}

ret = request_irq(gpmc_irq, gpmc_handle_irq, IRQF_SHARED, "gpmc", NULL);
ret = gpmc_setup_irq(gpmc_irq);
if (ret)
pr_err("gpmc: irq-%d could not claim: err %d\n",
gpmc_irq, ret);
Expand All @@ -780,12 +895,19 @@ postcore_initcall(gpmc_init);

static irqreturn_t gpmc_handle_irq(int irq, void *dev)
{
u8 cs;
int i;
u32 regval;

regval = gpmc_read_reg(GPMC_IRQSTATUS);

if (!regval)
return IRQ_NONE;

for (i = 0; i < GPMC_NR_IRQ; i++)
if (regval & gpmc_client_irq[i].bitmask)
generic_handle_irq(gpmc_client_irq[i].irq);

/* check cs to invoke the irq */
cs = ((gpmc_read_reg(GPMC_PREFETCH_CONFIG1)) >> CS_NUM_SHIFT) & 0x7;
if (OMAP_GPMC_IRQ_BASE+cs <= OMAP_GPMC_IRQ_END)
generic_handle_irq(OMAP_GPMC_IRQ_BASE+cs);
gpmc_write_reg(GPMC_IRQSTATUS, regval);

return IRQ_HANDLED;
}
Expand Down
19 changes: 19 additions & 0 deletions arch/arm/plat-omap/include/plat/gpmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,25 @@ struct gpmc_timings {
u16 wr_data_mux_bus; /* WRDATAONADMUXBUS */
};

struct gpmc_nand_regs {
void __iomem *gpmc_status;
void __iomem *gpmc_nand_command;
void __iomem *gpmc_nand_address;
void __iomem *gpmc_nand_data;
void __iomem *gpmc_prefetch_config1;
void __iomem *gpmc_prefetch_config2;
void __iomem *gpmc_prefetch_control;
void __iomem *gpmc_prefetch_status;
void __iomem *gpmc_ecc_config;
void __iomem *gpmc_ecc_control;
void __iomem *gpmc_ecc_size_config;
void __iomem *gpmc_ecc1_result;
void __iomem *gpmc_bch_result0;
};

extern void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs);
extern int gpmc_get_client_irq(unsigned irq_config);

extern unsigned int gpmc_ns_to_ticks(unsigned int time_ns);
extern unsigned int gpmc_ps_to_ticks(unsigned int time_ps);
extern unsigned int gpmc_ticks_to_ns(unsigned int ticks);
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/plat-omap/include/plat/nand.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ struct omap_nand_platform_data {
bool dev_ready;
int gpmc_irq;
enum nand_io xfer_type;
unsigned long phys_base;
int devsize;
enum omap_ecc ecc_opt;
struct gpmc_nand_regs reg;
};

/* minimum size for IO mapping */
Expand Down
Loading

0 comments on commit 5ec8d8c

Please sign in to comment.