|
@@ -574,3 +574,185 @@ int gpmc_cs_configure(int cs, int cmd, int wval)
|
|
|
gpmc_write_reg(GPMC_IRQSTATUS, wval);
|
|
|
break;
|
|
|
|
|
|
+ case GPMC_CONFIG_WP:
|
|
|
+ regval = gpmc_read_reg(GPMC_CONFIG);
|
|
|
+ if (wval)
|
|
|
+ regval &= ~GPMC_CONFIG_WRITEPROTECT; /* WP is ON */
|
|
|
+ else
|
|
|
+ regval |= GPMC_CONFIG_WRITEPROTECT; /* WP is OFF */
|
|
|
+ gpmc_write_reg(GPMC_CONFIG, regval);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case GPMC_CONFIG_RDY_BSY:
|
|
|
+ regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
|
|
|
+ if (wval)
|
|
|
+ regval |= WR_RD_PIN_MONITORING;
|
|
|
+ else
|
|
|
+ regval &= ~WR_RD_PIN_MONITORING;
|
|
|
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case GPMC_CONFIG_DEV_SIZE:
|
|
|
+ regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
|
|
|
+
|
|
|
+ /* clear 2 target bits */
|
|
|
+ regval &= ~GPMC_CONFIG1_DEVICESIZE(3);
|
|
|
+
|
|
|
+ /* set the proper value */
|
|
|
+ regval |= GPMC_CONFIG1_DEVICESIZE(wval);
|
|
|
+
|
|
|
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case GPMC_CONFIG_DEV_TYPE:
|
|
|
+ regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
|
|
|
+ regval |= GPMC_CONFIG1_DEVICETYPE(wval);
|
|
|
+ if (wval == GPMC_DEVICETYPE_NOR)
|
|
|
+ regval |= GPMC_CONFIG1_MUXADDDATA;
|
|
|
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ printk(KERN_ERR "gpmc_configure_cs: Not supported\n");
|
|
|
+ err = -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(gpmc_cs_configure);
|
|
|
+
|
|
|
+void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ for (i = 0; i < GPMC_BCH_NUM_REMAINDER; i++) {
|
|
|
+ reg->gpmc_bch_result0[i] = gpmc_base + GPMC_ECC_BCH_RESULT_0 +
|
|
|
+ GPMC_BCH_SIZE * i;
|
|
|
+ reg->gpmc_bch_result1[i] = gpmc_base + GPMC_ECC_BCH_RESULT_1 +
|
|
|
+ GPMC_BCH_SIZE * i;
|
|
|
+ reg->gpmc_bch_result2[i] = gpmc_base + GPMC_ECC_BCH_RESULT_2 +
|
|
|
+ GPMC_BCH_SIZE * i;
|
|
|
+ reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 +
|
|
|
+ GPMC_BCH_SIZE * i;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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(void)
|
|
|
+{
|
|
|
+ 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 int gpmc_free_irq(void)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (gpmc_irq)
|
|
|
+ free_irq(gpmc_irq, NULL);
|
|
|
+
|
|
|
+ for (i = 0; i < GPMC_NR_IRQ; i++) {
|
|
|
+ irq_set_handler(gpmc_client_irq[i].irq, NULL);
|
|
|
+ irq_set_chip(gpmc_client_irq[i].irq, &no_irq_chip);
|
|
|
+ irq_modify_status(gpmc_client_irq[i].irq, 0, 0);
|
|
|
+ }
|