| /* Cypress WestBridge OMAP3430 Kernel Hal source file (cyashalomap_kernel.c) |
| ## =========================== |
| ## Copyright (C) 2010 Cypress Semiconductor |
| ## |
| ## 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 program is distributed in the hope that it will be useful, |
| ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ## GNU General Public License for more details. |
| ## |
| ## 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., 51 Franklin Street, Fifth Floor, |
| ## Boston, MA 02110-1301, USA. |
| ## =========================== |
| */ |
| |
| #ifdef CONFIG_MACH_OMAP3_WESTBRIDGE_AST_PNAND_HAL |
| |
| #include <linux/fs.h> |
| #include <linux/ioport.h> |
| #include <linux/timer.h> |
| #include <linux/gpio.h> |
| #include <linux/interrupt.h> |
| #include <linux/delay.h> |
| #include <linux/scatterlist.h> |
| #include <linux/mm.h> |
| #include <linux/irq.h> |
| #include <linux/slab.h> |
| #include <linux/sched.h> |
| /* include seems broken moving for patch submission |
| * #include <mach/mux.h> |
| * #include <mach/gpmc.h> |
| * #include <mach/westbridge/westbridge-omap3-pnand-hal/cyashalomap_kernel.h> |
| * #include <mach/westbridge/westbridge-omap3-pnand-hal/cyasomapdev_kernel.h> |
| * #include <mach/westbridge/westbridge-omap3-pnand-hal/cyasmemmap.h> |
| * #include <linux/westbridge/cyaserr.h> |
| * #include <linux/westbridge/cyasregs.h> |
| * #include <linux/westbridge/cyasdma.h> |
| * #include <linux/westbridge/cyasintr.h> |
| */ |
| #include <linux/../../arch/arm/plat-omap/include/plat/mux.h> |
| #include <linux/../../arch/arm/plat-omap/include/plat/gpmc.h> |
| #include "../plat-omap/include/mach/westbridge/westbridge-omap3-pnand-hal/cyashalomap_kernel.h" |
| #include "../plat-omap/include/mach/westbridge/westbridge-omap3-pnand-hal/cyasomapdev_kernel.h" |
| #include "../plat-omap/include/mach/westbridge/westbridge-omap3-pnand-hal/cyasmemmap.h" |
| #include "../../../include/linux/westbridge/cyaserr.h" |
| #include "../../../include/linux/westbridge/cyasregs.h" |
| #include "../../../include/linux/westbridge/cyasdma.h" |
| #include "../../../include/linux/westbridge/cyasintr.h" |
| |
| #define HAL_REV "1.1.0" |
| |
| /* |
| * uncomment to enable 16bit pnand interface |
| */ |
| #define PNAND_16BIT_MODE |
| |
| /* |
| * selects one of 3 versions of pnand_lbd_read() |
| * PNAND_LBD_READ_NO_PFE - original 8/16 bit code |
| * reads through the gpmc CONTROLLER REGISTERS |
| * ENABLE_GPMC_PF_ENGINE - USES GPMC PFE FIFO reads, in 8 bit mode, |
| * same speed as the above |
| * PFE_LBD_READ_V2 - slightly diffrenet, performance same as above |
| */ |
| #define PNAND_LBD_READ_NO_PFE |
| /* #define ENABLE_GPMC_PF_ENGINE */ |
| /* #define PFE_LBD_READ_V2 */ |
| |
| /* |
| * westbrige astoria ISR options to limit number of |
| * back to back DMA transfers per ISR interrupt |
| */ |
| #define MAX_DRQ_LOOPS_IN_ISR 4 |
| |
| /* |
| * debug prints enabling |
| *#define DBGPRN_ENABLED |
| *#define DBGPRN_DMA_SETUP_RD |
| *#define DBGPRN_DMA_SETUP_WR |
| */ |
| |
| |
| /* |
| * For performance reasons, we handle storage endpoint transfers up to 4 KB |
| * within the HAL itself. |
| */ |
| #define CYASSTORAGE_WRITE_EP_NUM (4) |
| #define CYASSTORAGE_READ_EP_NUM (8) |
| |
| /* |
| * size of DMA packet HAL can accept from Storage API |
| * HAL will fragment it into smaller chunks that the P port can accept |
| */ |
| #define CYASSTORAGE_MAX_XFER_SIZE (2*32768) |
| |
| /* |
| * P port MAX DMA packet size according to interface/ep configurartion |
| */ |
| #define HAL_DMA_PKT_SZ 512 |
| |
| #define is_storage_e_p(ep) (((ep) == 2) || ((ep) == 4) || \ |
| ((ep) == 6) || ((ep) == 8)) |
| |
| /* |
| * persistent, stores current GPMC interface cfg mode |
| */ |
| static uint8_t pnand_16bit; |
| |
| /* |
| * keep processing new WB DRQ in ISR until all handled (performance feature) |
| */ |
| #define PROCESS_MULTIPLE_DRQ_IN_ISR (1) |
| |
| |
| /* |
| * ASTORIA PNAND IF COMMANDS, CASDO - READ, CASDI - WRITE |
| */ |
| #define CASDO 0x05 |
| #define CASDI 0x85 |
| #define RDPAGE_B1 0x00 |
| #define RDPAGE_B2 0x30 |
| #define PGMPAGE_B1 0x80 |
| #define PGMPAGE_B2 0x10 |
| |
| /* |
| * The type of DMA operation, per endpoint |
| */ |
| typedef enum cy_as_hal_dma_type { |
| cy_as_hal_read, |
| cy_as_hal_write, |
| cy_as_hal_none |
| } cy_as_hal_dma_type; |
| |
| |
| /* |
| * SG list halpers defined in scaterlist.h |
| #define sg_is_chain(sg) ((sg)->page_link & 0x01) |
| #define sg_is_last(sg) ((sg)->page_link & 0x02) |
| #define sg_chain_ptr(sg) \ |
| ((struct scatterlist *) ((sg)->page_link & ~0x03)) |
| */ |
| typedef struct cy_as_hal_endpoint_dma { |
| cy_bool buffer_valid; |
| uint8_t *data_p; |
| uint32_t size; |
| /* |
| * sg_list_enabled - if true use, r/w DMA transfers use sg list, |
| * FALSE use pointer to a buffer |
| * sg_p - pointer to the owner's sg list, of there is such |
| * (like blockdriver) |
| * dma_xfer_sz - size of the next dma xfer on P port |
| * seg_xfer_cnt - counts xfered bytes for in current sg_list |
| * memory segment |
| * req_xfer_cnt - total number of bytes transferred so far in |
| * current request |
| * req_length - total request length |
| */ |
| bool sg_list_enabled; |
| struct scatterlist *sg_p; |
| uint16_t dma_xfer_sz; |
| uint32_t seg_xfer_cnt; |
| uint16_t req_xfer_cnt; |
| uint16_t req_length; |
| cy_as_hal_dma_type type; |
| cy_bool pending; |
| } cy_as_hal_endpoint_dma; |
| |
| /* |
| * The list of OMAP devices (should be one) |
| */ |
| static cy_as_omap_dev_kernel *m_omap_list_p; |
| |
| /* |
| * The callback to call after DMA operations are complete |
| */ |
| static cy_as_hal_dma_complete_callback callback; |
| |
| /* |
| * Pending data size for the endpoints |
| */ |
| static cy_as_hal_endpoint_dma end_points[16]; |
| |
| /* |
| * Forward declaration |
| */ |
| static void cy_handle_d_r_q_interrupt(cy_as_omap_dev_kernel *dev_p); |
| |
| static uint16_t intr_sequence_num; |
| static uint8_t intr__enable; |
| spinlock_t int_lock; |
| |
| static u32 iomux_vma; |
| static u32 csa_phy; |
| |
| /* |
| * gpmc I/O registers VMA |
| */ |
| static u32 gpmc_base; |
| |
| /* |
| * gpmc data VMA associated with CS4 (ASTORIA CS on GPMC) |
| */ |
| static u32 gpmc_data_vma; |
| static u32 ndata_reg_vma; |
| static u32 ncmd_reg_vma; |
| static u32 naddr_reg_vma; |
| |
| /* |
| * fwd declarations |
| */ |
| static void p_nand_lbd_read(u16 col_addr, u32 row_addr, u16 count, void *buff); |
| static void p_nand_lbd_write(u16 col_addr, u32 row_addr, u16 count, void *buff); |
| static inline u16 __attribute__((always_inline)) |
| ast_p_nand_casdo_read(u8 reg_addr8); |
| static inline void __attribute__((always_inline)) |
| ast_p_nand_casdi_write(u8 reg_addr8, u16 data); |
| |
| /* |
| * prints given number of omap registers |
| */ |
| static void cy_as_hal_print_omap_regs(char *name_prefix, |
| u8 name_base, u32 virt_base, u16 count) |
| { |
| u32 reg_val, reg_addr; |
| u16 i; |
| cy_as_hal_print_message(KERN_INFO "\n"); |
| for (i = 0; i < count; i++) { |
| |
| reg_addr = virt_base + (i*4); |
| /* use virtual addresses here*/ |
| reg_val = __raw_readl(reg_addr); |
| cy_as_hal_print_message(KERN_INFO "%s_%d[%8.8x]=%8.8x\n", |
| name_prefix, name_base+i, |
| reg_addr, reg_val); |
| } |
| } |
| |
| /* |
| * setMUX function for a pad + additional pad flags |
| */ |
| static u16 omap_cfg_reg_L(u32 pad_func_index) |
| { |
| static u8 sanity_check = 1; |
| |
| u32 reg_vma; |
| u16 cur_val, wr_val, rdback_val; |
| |
| /* |
| * do sanity check on the omap_mux_pin_cfg[] table |
| */ |
| cy_as_hal_print_message(KERN_INFO" OMAP pins user_pad cfg "); |
| if (sanity_check) { |
| if ((omap_mux_pin_cfg[END_OF_TABLE].name[0] == 'E') && |
| (omap_mux_pin_cfg[END_OF_TABLE].name[1] == 'N') && |
| (omap_mux_pin_cfg[END_OF_TABLE].name[2] == 'D')) { |
| |
| cy_as_hal_print_message(KERN_INFO |
| "table is good.\n"); |
| } else { |
| cy_as_hal_print_message(KERN_WARNING |
| "table is bad, fix it"); |
| } |
| /* |
| * do it only once |
| */ |
| sanity_check = 0; |
| } |
| |
| /* |
| * get virtual address to the PADCNF_REG |
| */ |
| reg_vma = (u32)iomux_vma + omap_mux_pin_cfg[pad_func_index].offset; |
| |
| /* |
| * add additional USER PU/PD/EN flags |
| */ |
| wr_val = omap_mux_pin_cfg[pad_func_index].mux_val; |
| cur_val = IORD16(reg_vma); |
| |
| /* |
| * PADCFG regs 16 bit long, packed into 32 bit regs, |
| * can also be accessed as u16 |
| */ |
| IOWR16(reg_vma, wr_val); |
| rdback_val = IORD16(reg_vma); |
| |
| /* |
| * in case if the caller wants to save the old value |
| */ |
| return wr_val; |
| } |
| |
| #define BLKSZ_4K 0x1000 |
| |
| /* |
| * switch GPMC DATA bus mode |
| */ |
| void cy_as_hal_gpmc_enable_16bit_bus(bool dbus16_enabled) |
| { |
| uint32_t tmp32; |
| |
| /* |
| * disable gpmc CS4 operation 1st |
| */ |
| tmp32 = gpmc_cs_read_reg(AST_GPMC_CS, |
| GPMC_CS_CONFIG7) & ~GPMC_CONFIG7_CSVALID; |
| gpmc_cs_write_reg(AST_GPMC_CS, GPMC_CS_CONFIG7, tmp32); |
| |
| /* |
| * GPMC NAND data bus can be 8 or 16 bit wide |
| */ |
| if (dbus16_enabled) { |
| DBGPRN("enabling 16 bit bus\n"); |
| gpmc_cs_write_reg(AST_GPMC_CS, GPMC_CS_CONFIG1, |
| (GPMC_CONFIG1_DEVICETYPE(2) | |
| GPMC_CONFIG1_WAIT_PIN_SEL(2) | |
| GPMC_CONFIG1_DEVICESIZE_16) |
| ); |
| } else { |
| DBGPRN(KERN_INFO "enabling 8 bit bus\n"); |
| gpmc_cs_write_reg(AST_GPMC_CS, GPMC_CS_CONFIG1, |
| (GPMC_CONFIG1_DEVICETYPE(2) | |
| GPMC_CONFIG1_WAIT_PIN_SEL(2)) |
| ); |
| } |
| |
| /* |
| * re-enable astoria CS operation on GPMC |
| */ |
| gpmc_cs_write_reg(AST_GPMC_CS, GPMC_CS_CONFIG7, |
| (tmp32 | GPMC_CONFIG7_CSVALID)); |
| |
| /* |
| *remember the state |
| */ |
| pnand_16bit = dbus16_enabled; |
| } |
| |
| static int cy_as_hal_gpmc_init(void) |
| { |
| u32 tmp32; |
| int err; |
| struct gpmc_timings timings; |
| |
| gpmc_base = (u32)ioremap_nocache(OMAP34XX_GPMC_BASE, BLKSZ_4K); |
| DBGPRN(KERN_INFO "kernel has gpmc_base=%x , val@ the base=%x", |
| gpmc_base, __raw_readl(gpmc_base) |
| ); |
| |
| /* |
| * these are globals are full VMAs of the gpmc_base above |
| */ |
| ncmd_reg_vma = GPMC_VMA(AST_GPMC_NAND_CMD); |
| naddr_reg_vma = GPMC_VMA(AST_GPMC_NAND_ADDR); |
| ndata_reg_vma = GPMC_VMA(AST_GPMC_NAND_DATA); |
| |
| /* |
| * request GPMC CS for ASTORIA request |
| */ |
| if (gpmc_cs_request(AST_GPMC_CS, SZ_16M, (void *)&csa_phy) < 0) { |
| cy_as_hal_print_message(KERN_ERR "error failed to request" |
| "ncs4 for ASTORIA\n"); |
| return -1; |
| } else { |
| DBGPRN(KERN_INFO "got phy_addr:%x for " |
| "GPMC CS%d GPMC_CFGREG7[CS4]\n", |
| csa_phy, AST_GPMC_CS); |
| } |
| |
| /* |
| * request VM region for 4K addr space for chip select 4 phy address |
| * technically we don't need it for NAND devices, but do it anyway |
| * so that data read/write bus cycle can be triggered by reading |
| * or writing this mem region |
| */ |
| if (!request_mem_region(csa_phy, BLKSZ_4K, "AST_OMAP_HAL")) { |
| err = -EBUSY; |
| cy_as_hal_print_message(KERN_ERR "error MEM region " |
| "request for phy_addr:%x failed\n", |
| csa_phy); |
| goto out_free_cs; |
| } |
| |
| /* |
| * REMAP mem region associated with our CS |
| */ |
| gpmc_data_vma = (u32)ioremap_nocache(csa_phy, BLKSZ_4K); |
| if (!gpmc_data_vma) { |
| err = -ENOMEM; |
| cy_as_hal_print_message(KERN_ERR "error- ioremap()" |
| "for phy_addr:%x failed", csa_phy); |
| |
| goto out_release_mem_region; |
| } |
| cy_as_hal_print_message(KERN_INFO "ioremap(%x) returned vma=%x\n", |
| csa_phy, gpmc_data_vma); |
| |
| gpmc_cs_write_reg(AST_GPMC_CS, GPMC_CS_CONFIG1, |
| (GPMC_CONFIG1_DEVICETYPE(2) | |
| GPMC_CONFIG1_WAIT_PIN_SEL(2))); |
| |
| memset(&timings, 0, sizeof(timings)); |
| |
| /* cs timing */ |
| timings.cs_on = WB_GPMC_CS_t_o_n; |
| timings.cs_wr_off = WB_GPMC_BUSCYC_t; |
| timings.cs_rd_off = WB_GPMC_BUSCYC_t; |
| |
| /* adv timing */ |
| timings.adv_on = WB_GPMC_ADV_t_o_n; |
| timings.adv_rd_off = WB_GPMC_BUSCYC_t; |
| timings.adv_wr_off = WB_GPMC_BUSCYC_t; |
| |
| /* oe timing */ |
| timings.oe_on = WB_GPMC_OE_t_o_n; |
| timings.oe_off = WB_GPMC_OE_t_o_f_f; |
| timings.access = WB_GPMC_RD_t_a_c_c; |
| timings.rd_cycle = WB_GPMC_BUSCYC_t; |
| |
| /* we timing */ |
| timings.we_on = WB_GPMC_WE_t_o_n; |
| timings.we_off = WB_GPMC_WE_t_o_f_f; |
| timings.wr_access = WB_GPMC_WR_t_a_c_c; |
| timings.wr_cycle = WB_GPMC_BUSCYC_t; |
| |
| timings.page_burst_access = WB_GPMC_BUSCYC_t; |
| timings.wr_data_mux_bus = WB_GPMC_BUSCYC_t; |
| gpmc_cs_set_timings(AST_GPMC_CS, &timings); |
| |
| cy_as_hal_print_omap_regs("GPMC_CONFIG", 1, |
| GPMC_VMA(GPMC_CFG_REG(1, AST_GPMC_CS)), 7); |
| |
| /* |
| * DISABLE cs4, NOTE GPMC REG7 is already configured |
| * at this point by gpmc_cs_request |
| */ |
| tmp32 = gpmc_cs_read_reg(AST_GPMC_CS, GPMC_CS_CONFIG7) & |
| ~GPMC_CONFIG7_CSVALID; |
| gpmc_cs_write_reg(AST_GPMC_CS, GPMC_CS_CONFIG7, tmp32); |
| |
| /* |
| * PROGRAM chip select Region, (see OMAP3430 TRM PAGE 1088) |
| */ |
| gpmc_cs_write_reg(AST_GPMC_CS, GPMC_CS_CONFIG7, |
| (AS_CS_MASK | AS_CS_BADDR)); |
| |
| /* |
| * by default configure GPMC into 8 bit mode |
| * (to match astoria default mode) |
| */ |
| gpmc_cs_write_reg(AST_GPMC_CS, GPMC_CS_CONFIG1, |
| (GPMC_CONFIG1_DEVICETYPE(2) | |
| GPMC_CONFIG1_WAIT_PIN_SEL(2))); |
| |
| /* |
| * ENABLE astoria cs operation on GPMC |
| */ |
| gpmc_cs_write_reg(AST_GPMC_CS, GPMC_CS_CONFIG7, |
| (tmp32 | GPMC_CONFIG7_CSVALID)); |
| |
| /* |
| * No method currently exists to write this register through GPMC APIs |
| * need to change WAIT2 polarity |
| */ |
| tmp32 = IORD32(GPMC_VMA(GPMC_CONFIG_REG)); |
| tmp32 = tmp32 | NAND_FORCE_POSTED_WRITE_B | 0x40; |
| IOWR32(GPMC_VMA(GPMC_CONFIG_REG), tmp32); |
| |
| tmp32 = IORD32(GPMC_VMA(GPMC_CONFIG_REG)); |
| cy_as_hal_print_message("GPMC_CONFIG_REG=0x%x\n", tmp32); |
| |
| return 0; |
| |
| out_release_mem_region: |
| release_mem_region(csa_phy, BLKSZ_4K); |
| |
| out_free_cs: |
| gpmc_cs_free(AST_GPMC_CS); |
| |
| return err; |
| } |
| |
| /* |
| * west bridge astoria ISR (Interrupt handler) |
| */ |
| static irqreturn_t cy_astoria_int_handler(int irq, |
| void *dev_id, struct pt_regs *regs) |
| { |
| cy_as_omap_dev_kernel *dev_p; |
| uint16_t read_val = 0; |
| uint16_t mask_val = 0; |
| |
| /* |
| * debug stuff, counts number of loops per one intr trigger |
| */ |
| uint16_t drq_loop_cnt = 0; |
| uint8_t irq_pin; |
| /* |
| * flags to watch |
| */ |
| const uint16_t sentinel = (CY_AS_MEM_P0_INTR_REG_MCUINT | |
| CY_AS_MEM_P0_INTR_REG_MBINT | |
| CY_AS_MEM_P0_INTR_REG_PMINT | |
| CY_AS_MEM_P0_INTR_REG_PLLLOCKINT); |
| |
| /* |
| * sample IRQ pin level (just for statistics) |
| */ |
| irq_pin = __gpio_get_value(AST_INT); |
| |
| /* |
| * this one just for debugging |
| */ |
| intr_sequence_num++; |
| |
| /* |
| * astoria device handle |
| */ |
| dev_p = dev_id; |
| |
| /* |
| * read Astoria intr register |
| */ |
| read_val = cy_as_hal_read_register((cy_as_hal_device_tag)dev_p, |
| CY_AS_MEM_P0_INTR_REG); |
| |
| /* |
| * save current mask value |
| */ |
| mask_val = cy_as_hal_read_register((cy_as_hal_device_tag)dev_p, |
| CY_AS_MEM_P0_INT_MASK_REG); |
| |
| DBGPRN("<1>HAL__intr__enter:_seq:%d, P0_INTR_REG:%x\n", |
| intr_sequence_num, read_val); |
| |
| /* |
| * Disable WB interrupt signal generation while we are in ISR |
| */ |
| cy_as_hal_write_register((cy_as_hal_device_tag)dev_p, |
| CY_AS_MEM_P0_INT_MASK_REG, 0x0000); |
| |
| /* |
| * this is a DRQ Interrupt |
| */ |
| if (read_val & CY_AS_MEM_P0_INTR_REG_DRQINT) { |
| |
| do { |
| /* |
| * handle DRQ interrupt |
| */ |
| drq_loop_cnt++; |
| |
| cy_handle_d_r_q_interrupt(dev_p); |
| |
| /* |
| * spending to much time in ISR may impact |
| * average system performance |
| */ |
| if (drq_loop_cnt >= MAX_DRQ_LOOPS_IN_ISR) |
| break; |
| |
| /* |
| * Keep processing if there is another DRQ int flag |
| */ |
| } while (cy_as_hal_read_register((cy_as_hal_device_tag)dev_p, |
| CY_AS_MEM_P0_INTR_REG) & |
| CY_AS_MEM_P0_INTR_REG_DRQINT); |
| } |
| |
| if (read_val & sentinel) |
| cy_as_intr_service_interrupt((cy_as_hal_device_tag)dev_p); |
| |
| DBGPRN("<1>_hal:_intr__exit seq:%d, mask=%4.4x," |
| "int_pin:%d DRQ_jobs:%d\n", |
| intr_sequence_num, |
| mask_val, |
| irq_pin, |
| drq_loop_cnt); |
| |
| /* |
| * re-enable WB hw interrupts |
| */ |
| cy_as_hal_write_register((cy_as_hal_device_tag)dev_p, |
| CY_AS_MEM_P0_INT_MASK_REG, mask_val); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int cy_as_hal_configure_interrupts(void *dev_p) |
| { |
| int result; |
| int irq_pin = AST_INT; |
| |
| irq_set_irq_type(OMAP_GPIO_IRQ(irq_pin), IRQ_TYPE_LEVEL_LOW); |
| |
| /* |
| * for shared IRQS must provide non NULL device ptr |
| * othervise the int won't register |
| * */ |
| result = request_irq(OMAP_GPIO_IRQ(irq_pin), |
| (irq_handler_t)cy_astoria_int_handler, |
| IRQF_SHARED, "AST_INT#", dev_p); |
| |
| if (result == 0) { |
| /* |
| * OMAP_GPIO_IRQ(irq_pin) - omap logical IRQ number |
| * assigned to this interrupt |
| * OMAP_GPIO_BIT(AST_INT, GPIO_IRQENABLE1) - print status |
| * of AST_INT GPIO IRQ_ENABLE FLAG |
| */ |
| cy_as_hal_print_message(KERN_INFO"AST_INT omap_pin:" |
| "%d assigned IRQ #%d IRQEN1=%d\n", |
| irq_pin, |
| OMAP_GPIO_IRQ(irq_pin), |
| OMAP_GPIO_BIT(AST_INT, GPIO_IRQENABLE1) |
| ); |
| } else { |
| cy_as_hal_print_message("cyasomaphal: interrupt " |
| "failed to register\n"); |
| gpio_free(irq_pin); |
| cy_as_hal_print_message(KERN_WARNING |
| "ASTORIA: can't get assigned IRQ" |
| "%i for INT#\n", OMAP_GPIO_IRQ(irq_pin)); |
| } |
| |
| return result; |
| } |
| |
| /* |
| * initialize OMAP pads/pins to user defined functions |
| */ |
| static void cy_as_hal_init_user_pads(user_pad_cfg_t *pad_cfg_tab) |
| { |
| /* |
| * browse through the table an dinitiaze the pins |
| */ |
| u32 in_level = 0; |
| u16 tmp16, mux_val; |
| |
| while (pad_cfg_tab->name != NULL) { |
| |
| if (gpio_request(pad_cfg_tab->pin_num, NULL) == 0) { |
| |
| pad_cfg_tab->valid = 1; |
| mux_val = omap_cfg_reg_L(pad_cfg_tab->mux_func); |
| |
| /* |
| * always set drv level before changing out direction |
| */ |
| __gpio_set_value(pad_cfg_tab->pin_num, |
| pad_cfg_tab->drv); |
| |
| /* |
| * "0" - OUT, "1", input omap_set_gpio_direction |
| * (pad_cfg_tab->pin_num, pad_cfg_tab->dir); |
| */ |
| if (pad_cfg_tab->dir) |
| gpio_direction_input(pad_cfg_tab->pin_num); |
| else |
| gpio_direction_output(pad_cfg_tab->pin_num, |
| pad_cfg_tab->drv); |
| |
| /* sample the pin */ |
| in_level = __gpio_get_value(pad_cfg_tab->pin_num); |
| |
| cy_as_hal_print_message(KERN_INFO "configured %s to " |
| "OMAP pad_%d, DIR=%d " |
| "DOUT=%d, DIN=%d\n", |
| pad_cfg_tab->name, |
| pad_cfg_tab->pin_num, |
| pad_cfg_tab->dir, |
| pad_cfg_tab->drv, |
| in_level |
| ); |
| } else { |
| /* |
| * get the pad_mux value to check on the pin_function |
| */ |
| cy_as_hal_print_message(KERN_INFO "couldn't cfg pin %d" |
| "for signal %s, its already taken\n", |
| pad_cfg_tab->pin_num, |
| pad_cfg_tab->name); |
| } |
| |
| tmp16 = *(u16 *)PADCFG_VMA |
| (omap_mux_pin_cfg[pad_cfg_tab->mux_func].offset); |
| |
| cy_as_hal_print_message(KERN_INFO "GPIO_%d(PAD_CFG=%x,OE=%d" |
| "DOUT=%d, DIN=%d IRQEN=%d)\n\n", |
| pad_cfg_tab->pin_num, tmp16, |
| OMAP_GPIO_BIT(pad_cfg_tab->pin_num, GPIO_OE), |
| OMAP_GPIO_BIT(pad_cfg_tab->pin_num, GPIO_DATA_OUT), |
| OMAP_GPIO_BIT(pad_cfg_tab->pin_num, GPIO_DATA_IN), |
| OMAP_GPIO_BIT(pad_cfg_tab->pin_num, GPIO_IRQENABLE1) |
| ); |
| |
| /* |
| * next pad_cfg deriptor |
| */ |
| pad_cfg_tab++; |
| } |
| |
| cy_as_hal_print_message(KERN_INFO"pads configured\n"); |
| } |
| |
| |
| /* |
| * release gpios taken by the module |
| */ |
| static void cy_as_hal_release_user_pads(user_pad_cfg_t *pad_cfg_tab) |
| { |
| while (pad_cfg_tab->name != NULL) { |
| |
| if (pad_cfg_tab->valid) { |
| gpio_free(pad_cfg_tab->pin_num); |
| pad_cfg_tab->valid = 0; |
| cy_as_hal_print_message(KERN_INFO "GPIO_%d " |
| "released from %s\n", |
| pad_cfg_tab->pin_num, |
| pad_cfg_tab->name); |
| } else { |
| cy_as_hal_print_message(KERN_INFO "no release " |
| "for %s, GPIO_%d, wasn't acquired\n", |
| pad_cfg_tab->name, |
| pad_cfg_tab->pin_num); |
| } |
| pad_cfg_tab++; |
| } |
| } |
| |
| void cy_as_hal_config_c_s_mux(void) |
| { |
| /* |
| * FORCE the GPMC CS4 pin (it is in use by the zoom system) |
| */ |
| omap_cfg_reg_L(T8_OMAP3430_GPMC_n_c_s4); |
| } |
| EXPORT_SYMBOL(cy_as_hal_config_c_s_mux); |
| |
| /* |
| * inits all omap h/w |
| */ |
| uint32_t cy_as_hal_processor_hw_init(void) |
| { |
| int i, err; |
| |
| cy_as_hal_print_message(KERN_INFO "init OMAP3430 hw...\n"); |
| |
| iomux_vma = (u32)ioremap_nocache( |
| (u32)CTLPADCONF_BASE_ADDR, CTLPADCONF_SIZE); |
| cy_as_hal_print_message(KERN_INFO "PADCONF_VMA=%x val=%x\n", |
| iomux_vma, IORD32(iomux_vma)); |
| |
| /* |
| * remap gpio banks |
| */ |
| for (i = 0; i < 6; i++) { |
| gpio_vma_tab[i].virt_addr = (u32)ioremap_nocache( |
| gpio_vma_tab[i].phy_addr, |
| gpio_vma_tab[i].size); |
| |
| cy_as_hal_print_message(KERN_INFO "%s virt_addr=%x\n", |
| gpio_vma_tab[i].name, |
| (u32)gpio_vma_tab[i].virt_addr); |
| }; |
| |
| /* |
| * force OMAP_GPIO_126 to rleased state, |
| * will be configured to drive reset |
| */ |
| gpio_free(AST_RESET); |
| |
| /* |
| *same thing with AStoria CS pin |
| */ |
| gpio_free(AST_CS); |
| |
| /* |
| * initialize all the OMAP pads connected to astoria |
| */ |
| cy_as_hal_init_user_pads(user_pad_cfg); |
| |
| err = cy_as_hal_gpmc_init(); |
| if (err < 0) |
| cy_as_hal_print_message(KERN_INFO"gpmc init failed:%d", err); |
| |
| cy_as_hal_config_c_s_mux(); |
| |
| return gpmc_data_vma; |
| } |
| EXPORT_SYMBOL(cy_as_hal_processor_hw_init); |
| |
| void cy_as_hal_omap_hardware_deinit(cy_as_omap_dev_kernel *dev_p) |
| { |
| /* |
| * free omap hw resources |
| */ |
| if (gpmc_data_vma != 0) |
| iounmap((void *)gpmc_data_vma); |
| |
| if (csa_phy != 0) |
| release_mem_region(csa_phy, BLKSZ_4K); |
| |
| gpmc_cs_free(AST_GPMC_CS); |
| |
| free_irq(OMAP_GPIO_IRQ(AST_INT), dev_p); |
| |
| cy_as_hal_release_user_pads(user_pad_cfg); |
| } |
| |
| /* |
| * These are the functions that are not part of the |
| * HAL layer, but are required to be called for this HAL |
| */ |
| |
| /* |
| * Called On AstDevice LKM exit |
| */ |
| int stop_o_m_a_p_kernel(const char *pgm, cy_as_hal_device_tag tag) |
| { |
| cy_as_omap_dev_kernel *dev_p = (cy_as_omap_dev_kernel *)tag; |
| |
| /* |
| * TODO: Need to disable WB interrupt handlere 1st |
| */ |
| if (0 == dev_p) |
| return 1; |
| |
| cy_as_hal_print_message("<1>_stopping OMAP34xx HAL layer object\n"); |
| if (dev_p->m_sig != CY_AS_OMAP_KERNEL_HAL_SIG) { |
| cy_as_hal_print_message("<1>%s: %s: bad HAL tag\n", |
| pgm, __func__); |
| return 1; |
| } |
| |
| /* |
| * disable interrupt |
| */ |
| cy_as_hal_write_register((cy_as_hal_device_tag)dev_p, |
| CY_AS_MEM_P0_INT_MASK_REG, 0x0000); |
| |
| #if 0 |
| if (dev_p->thread_flag == 0) { |
| dev_p->thread_flag = 1; |
| wait_for_completion(&dev_p->thread_complete); |
| cy_as_hal_print_message("cyasomaphal:" |
| "done cleaning thread\n"); |
| cy_as_hal_destroy_sleep_channel(&dev_p->thread_sc); |
| } |
| #endif |
| |
| cy_as_hal_omap_hardware_deinit(dev_p); |
| |
| /* |
| * Rearrange the list |
| */ |
| if (m_omap_list_p == dev_p) |
| m_omap_list_p = dev_p->m_next_p; |
| |
| cy_as_hal_free(dev_p); |
| |
| cy_as_hal_print_message(KERN_INFO"OMAP_kernel_hal stopped\n"); |
| return 0; |
| } |
| |
| int omap_start_intr(cy_as_hal_device_tag tag) |
| { |
| cy_as_omap_dev_kernel *dev_p = (cy_as_omap_dev_kernel *)tag; |
| int ret = 0; |
| const uint16_t mask = CY_AS_MEM_P0_INTR_REG_DRQINT | |
| CY_AS_MEM_P0_INTR_REG_MBINT; |
| |
| /* |
| * register for interrupts |
| */ |
| ret = cy_as_hal_configure_interrupts(dev_p); |
| |
| /* |
| * enable only MBox & DRQ interrupts for now |
| */ |
| cy_as_hal_write_register((cy_as_hal_device_tag)dev_p, |
| CY_AS_MEM_P0_INT_MASK_REG, mask); |
| |
| return 1; |
| } |
| |
| /* |
| * Below are the functions that communicate with the WestBridge device. |
| * These are system dependent and must be defined by the HAL layer |
| * for a given system. |
| */ |
| |
| /* |
| * GPMC NAND command+addr write phase |
| */ |
| static inline void nand_cmd_n_addr(u8 cmdb1, u16 col_addr, u32 row_addr) |
| { |
| /* |
| * byte order on the bus <cmd> <CA0,CA1,RA0,RA1, RA2> |
| */ |
| u32 tmpa32 = ((row_addr << 16) | col_addr); |
| u8 RA2 = (u8)(row_addr >> 16); |
| |
| if (!pnand_16bit) { |
| /* |
| * GPMC PNAND 8bit BUS |
| */ |
| /* |
| * CMD1 |
| */ |
| IOWR8(ncmd_reg_vma, cmdb1); |
| |
| /* |
| *pnand bus: <CA0,CA1,RA0,RA1> |
| */ |
| IOWR32(naddr_reg_vma, tmpa32); |
| |
| /* |
| * <RA2> , always zero |
| */ |
| IOWR8(naddr_reg_vma, RA2); |
| |
| } else { |
| /* |
| * GPMC PNAND 16bit BUS , in 16 bit mode CMD |
| * and ADDR sent on [d7..d0] |
| */ |
| uint8_t CA0, CA1, RA0, RA1; |
| CA0 = tmpa32 & 0x000000ff; |
| CA1 = (tmpa32 >> 8) & 0x000000ff; |
| RA0 = (tmpa32 >> 16) & 0x000000ff; |
| RA1 = (tmpa32 >> 24) & 0x000000ff; |
| |
| /* |
| * can't use 32 bit writes here omap will not serialize |
| * them to lower half in16 bit mode |
| */ |
| |
| /* |
| *pnand bus: <CMD1, CA0,CA1,RA0,RA1, RA2 (always zero)> |
| */ |
| IOWR8(ncmd_reg_vma, cmdb1); |
| IOWR8(naddr_reg_vma, CA0); |
| IOWR8(naddr_reg_vma, CA1); |
| IOWR8(naddr_reg_vma, RA0); |
| IOWR8(naddr_reg_vma, RA1); |
| IOWR8(naddr_reg_vma, RA2); |
| } |
| } |
| |
| /* |
| * spin until r/b goes high |
| */ |
| inline int wait_rn_b_high(void) |
| { |
| u32 w_spins = 0; |
| |
| /* |
| * TODO: note R/b may go low here, need to spin until high |
| * while (omap_get_gpio_datain(AST_RnB) == 0) { |
| * w_spins++; |
| * } |
| * if (OMAP_GPIO_BIT(AST_RnB, GPIO_DATA_IN) == 0) { |
| * |
| * while (OMAP_GPIO_BIT(AST_RnB, GPIO_DATA_IN) == 0) { |
| * w_spins++; |
| * } |
| * printk("<1>RnB=0!:%d\n",w_spins); |
| * } |
| */ |
| return w_spins; |
| } |
| |
| #ifdef ENABLE_GPMC_PF_ENGINE |
| /* #define PFE_READ_DEBUG |
| * PNAND block read with OMAP PFE enabled |
| * status: Not tested, NW, broken , etc |
| */ |
| static void p_nand_lbd_read(u16 col_addr, u32 row_addr, u16 count, void *buff) |
| { |
| uint16_t w32cnt; |
| uint32_t *ptr32; |
| uint8_t *ptr8; |
| uint8_t bytes_in_fifo; |
| |
| /* debug vars*/ |
| #ifdef PFE_READ_DEBUG |
| uint32_t loop_limit; |
| uint16_t bytes_read = 0; |
| #endif |
| |
| /* |
| * configure the prefetch engine |
| */ |
| uint32_t tmp32; |
| uint32_t pfe_status; |
| |
| /* |
| * DISABLE GPMC CS4 operation 1st, this is |
| * in case engine is be already disabled |
| */ |
| IOWR32(GPMC_VMA(GPMC_PREFETCH_CONTROL), 0x0); |
| IOWR32(GPMC_VMA(GPMC_PREFETCH_CONFIG1), GPMC_PREFETCH_CONFIG1_VAL); |
| IOWR32(GPMC_VMA(GPMC_PREFETCH_CONFIG2), count); |
| |
| #ifdef PFE_READ_DEBUG |
| tmp32 = IORD32(GPMC_VMA(GPMC_PREFETCH_CONFIG1)); |
| if (tmp32 != GPMC_PREFETCH_CONFIG1_VAL) { |
| printk(KERN_INFO "<1> prefetch is CONFIG1 read val:%8.8x, != VAL written:%8.8x\n", |
| tmp32, GPMC_PREFETCH_CONFIG1_VAL); |
| tmp32 = IORD32(GPMC_VMA(GPMC_PREFETCH_STATUS)); |
| printk(KERN_INFO "<1> GPMC_PREFETCH_STATUS : %8.8x\n", tmp32); |
| } |
| |
| /* |
| *sanity check 2 |
| */ |
| tmp32 = IORD32(GPMC_VMA(GPMC_PREFETCH_CONFIG2)); |
| if (tmp32 != (count)) |
| printk(KERN_INFO "<1> GPMC_PREFETCH_CONFIG2 read val:%d, " |
| "!= VAL written:%d\n", tmp32, count); |
| #endif |
| |
| /* |
| * ISSUE PNAND CMD+ADDR, note gpmc puts 32b words |
| * on the bus least sig. byte 1st |
| */ |
| nand_cmd_n_addr(RDPAGE_B1, col_addr, row_addr); |
| |
| IOWR8(ncmd_reg_vma, RDPAGE_B2); |
| |
| /* |
| * start the prefetch engine |
| */ |
| IOWR32(GPMC_VMA(GPMC_PREFETCH_CONTROL), 0x1); |
| |
| ptr32 = buff; |
| |
| while (1) { |
| /* |
| * GPMC PFE service loop |
| */ |
| do { |
| /* |
| * spin until PFE fetched some |
| * PNAND bus words in the FIFO |
| */ |
| pfe_status = IORD32(GPMC_VMA(GPMC_PREFETCH_STATUS)); |
| bytes_in_fifo = (pfe_status >> 24) & 0x7f; |
| } while (bytes_in_fifo == 0); |
| |
| /* whole 32 bit words in fifo */ |
| w32cnt = bytes_in_fifo >> 2; |
| |
| #if 0 |
| /* |
| *NOTE: FIFO_PTR indicates number of NAND bus words bytes |
| * already received in the FIFO and available to be read |
| * by DMA or MPU whether COUNTVAL indicates number of BUS |
| * words yet to be read from PNAND bus words |
| */ |
| printk(KERN_ERR "<1> got PF_STATUS:%8.8x FIFO_PTR:%d, COUNTVAL:%d, w32cnt:%d\n", |
| pfe_status, bytes_in_fifo, |
| (pfe_status & 0x3fff), w32cnt); |
| #endif |
| |
| while (w32cnt--) |
| *ptr32++ = IORD32(gpmc_data_vma); |
| |
| if ((pfe_status & 0x3fff) == 0) { |
| /* |
| * PFE acc angine done, there still may be data leftover |
| * in the FIFO re-read FIFO BYTE counter (check for |
| * leftovers from 32 bit read accesses above) |
| */ |
| bytes_in_fifo = (IORD32( |
| GPMC_VMA(GPMC_PREFETCH_STATUS)) >> 24) & 0x7f; |
| |
| /* |
| * NOTE we may still have one word left in the fifo |
| * read it out |
| */ |
| ptr8 = ptr32; |
| switch (bytes_in_fifo) { |
| |
| case 0: |
| /* |
| * nothing to do we already read the |
| * FIFO out with 32 bit accesses |
| */ |
| break; |
| case 1: |
| /* |
| * this only possible |
| * for 8 bit pNAND only |
| */ |
| *ptr8 = IORD8(gpmc_data_vma); |
| break; |
| |
| case 2: |
| /* |
| * this one can occur in either modes |
| */ |
| *(uint16_t *)ptr8 = IORD16(gpmc_data_vma); |
| break; |
| |
| case 3: |
| /* |
| * this only possible for 8 bit pNAND only |
| */ |
| *(uint16_t *)ptr8 = IORD16(gpmc_data_vma); |
| ptr8 += 2; |
| *ptr8 = IORD8(gpmc_data_vma); |
| break; |
| |
| case 4: |
| /* |
| * shouldn't happen, but has been seen |
| * in 8 bit mode |
| */ |
| *ptr32 = IORD32(gpmc_data_vma); |
| break; |
| |
| default: |
| printk(KERN_ERR"<1>_error: PFE FIFO bytes leftover is not read:%d\n", |
| bytes_in_fifo); |
| break; |
| } |
| /* |
| * read is completed, get out of the while(1) loop |
| */ |
| break; |
| } |
| } |
| } |
| #endif |
| |
| #ifdef PFE_LBD_READ_V2 |
| /* |
| * PFE engine assisted reads with the 64 byte blocks |
| */ |
| static void p_nand_lbd_read(u16 col_addr, u32 row_addr, u16 count, void *buff) |
| { |
| uint8_t rd_cnt; |
| uint32_t *ptr32; |
| uint8_t *ptr8; |
| uint16_t reminder; |
| uint32_t pfe_status; |
| |
| /* |
| * ISSUE PNAND CMD+ADDR |
| * note gpmc puts 32b words on the bus least sig. byte 1st |
| */ |
| nand_cmd_n_addr(RDPAGE_B1, col_addr, row_addr); |
| IOWR8(ncmd_reg_vma, RDPAGE_B2); |
| |
| /* |
| * setup PFE block |
| * count - OMAP number of bytes to access on pnand bus |
| */ |
| |
| IOWR32(GPMC_VMA(GPMC_PREFETCH_CONFIG1), GPMC_PREFETCH_CONFIG1_VAL); |
| IOWR32(GPMC_VMA(GPMC_PREFETCH_CONFIG2), count); |
| IOWR32(GPMC_VMA(GPMC_PREFETCH_CONTROL), 0x1); |
| |
| ptr32 = buff; |
| |
| do { |
| pfe_status = IORD32(GPMC_VMA(GPMC_PREFETCH_STATUS)); |
| rd_cnt = pfe_status >> (24+2); |
| |
| while (rd_cnt--) |
| *ptr32++ = IORD32(gpmc_data_vma); |
| |
| } while (pfe_status & 0x3fff); |
| |
| /* |
| * read out the leftover |
| */ |
| ptr8 = ptr32; |
| rd_cnt = (IORD32(GPMC_VMA(GPMC_PREFETCH_STATUS)) >> 24) & 0x7f; |
| |
| while (rd_cnt--) |
| *ptr8++ = IORD8(gpmc_data_vma); |
| } |
| #endif |
| |
| #ifdef PNAND_LBD_READ_NO_PFE |
| /* |
| * Endpoint buffer read w/o OMAP GPMC Prefetch Engine |
| * the original working code, works at max speed for 8 bit xfers |
| * for 16 bit the bus diagram has gaps |
| */ |
| static void p_nand_lbd_read(u16 col_addr, u32 row_addr, u16 count, void *buff) |
| { |
| uint16_t w32cnt; |
| uint32_t *ptr32; |
| uint16_t *ptr16; |
| uint16_t remainder; |
| |
| DBGPRN("<1> %s(): NO_PFE\n", __func__); |
| |
| ptr32 = buff; |
| /* number of whole 32 bit words in the transfer */ |
| w32cnt = count >> 2; |
| |
| /* remainder, in bytes(0..3) */ |
| remainder = count & 03; |
| |
| /* |
| * note gpmc puts 32b words on the bus least sig. byte 1st |
| */ |
| nand_cmd_n_addr(RDPAGE_B1, col_addr, row_addr); |
| IOWR8(ncmd_reg_vma, RDPAGE_B2); |
| |
| /* |
| * read data by 32 bit chunks |
| */ |
| while (w32cnt--) |
| *ptr32++ = IORD32(ndata_reg_vma); |
| |
| /* |
| * now do the remainder(it can be 0, 1, 2 or 3) |
| * same code for both 8 & 16 bit bus |
| * do 1 or 2 MORE words |
| */ |
| ptr16 = (uint16_t *)ptr32; |
| |
| switch (remainder) { |
| case 1: |
| /* read one 16 bit word |
| * IN 8 BIT WE NEED TO READ even number of bytes |
| */ |
| case 2: |
| *ptr16 = IORD16(ndata_reg_vma); |
| break; |
| case 3: |
| /* |
| * for 3 bytes read 2 16 bit words |
| */ |
| *ptr16++ = IORD16(ndata_reg_vma); |
| *ptr16 = IORD16(ndata_reg_vma); |
| break; |
| default: |
| /* |
| * remainder is 0 |
| */ |
| break; |
| } |
| } |
| #endif |
| |
| /* |
| * uses LBD mode to write N bytes into astoria |
| * Status: Working, however there are 150ns idle |
| * timeafter every 2 (16 bit or 4(8 bit) bus cycles |
| */ |
| static void p_nand_lbd_write(u16 col_addr, u32 row_addr, u16 count, void *buff) |
| { |
| uint16_t w32cnt; |
| uint16_t remainder; |
| uint8_t *ptr8; |
| uint16_t *ptr16; |
| uint32_t *ptr32; |
| |
| remainder = count & 03; |
| w32cnt = count >> 2; |
| ptr32 = buff; |
| ptr8 = buff; |
| |
| /* |
| * send: CMDB1, CA0,CA1,RA0,RA1,RA2 |
| */ |
| nand_cmd_n_addr(PGMPAGE_B1, col_addr, row_addr); |
| |
| /* |
| * blast the data out in 32bit chunks |
| */ |
| while (w32cnt--) |
| IOWR32(ndata_reg_vma, *ptr32++); |
| |
| /* |
| * do the reminder if there is one |
| * same handling for both 8 & 16 bit pnand: mode |
| */ |
| ptr16 = (uint16_t *)ptr32; /* do 1 or 2 words */ |
| |
| switch (remainder) { |
| case 1: |
| /* |
| * read one 16 bit word |
| */ |
| case 2: |
| IOWR16(ndata_reg_vma, *ptr16); |
| break; |
| |
| case 3: |
| /* |
| * for 3 bytes read 2 16 bit words |
| */ |
| IOWR16(ndata_reg_vma, *ptr16++); |
| IOWR16(ndata_reg_vma, *ptr16); |
| break; |
| default: |
| /* |
| * reminder is 0 |
| */ |
| break; |
| } |
| /* |
| * finally issue a PGM cmd |
| */ |
| IOWR8(ncmd_reg_vma, PGMPAGE_B2); |
| } |
| |
| /* |
| * write Astoria register |
| */ |
| static inline void ast_p_nand_casdi_write(u8 reg_addr8, u16 data) |
| { |
| unsigned long flags; |
| u16 addr16; |
| /* |
| * throw an error if called from multiple threads |
| */ |
| static atomic_t rdreg_usage_cnt = { 0 }; |
| |
| /* |
| * disable interrupts |
| */ |
| local_irq_save(flags); |
| |
| if (atomic_read(&rdreg_usage_cnt) != 0) { |
| cy_as_hal_print_message(KERN_ERR "cy_as_omap_hal:" |
| "* cy_as_hal_write_register usage:%d\n", |
| atomic_read(&rdreg_usage_cnt)); |
| } |
| |
| atomic_inc(&rdreg_usage_cnt); |
| |
| /* |
| * 2 flavors of GPMC -> PNAND access |
| */ |
| if (pnand_16bit) { |
| /* |
| * 16 BIT gpmc NAND mode |
| */ |
| |
| /* |
| * CMD1, CA1, CA2, |
| */ |
| IOWR8(ncmd_reg_vma, 0x85); |
| IOWR8(naddr_reg_vma, reg_addr8); |
| IOWR8(naddr_reg_vma, 0x0c); |
| |
| /* |
| * this should be sent on the 16 bit bus |
| */ |
| IOWR16(ndata_reg_vma, data); |
| } else { |
| /* |
| * 8 bit nand mode GPMC will automatically |
| * seriallize 16bit or 32 bit writes into |
| * 8 bit onesto the lower 8 bit in LE order |
| */ |
| addr16 = 0x0c00 | reg_addr8; |
| |
| /* |
| * CMD1, CA1, CA2, |
| */ |
| IOWR8(ncmd_reg_vma, 0x85); |
| IOWR16(naddr_reg_vma, addr16); |
| IOWR16(ndata_reg_vma, data); |
| } |
| |
| /* |
| * re-enable interrupts |
| */ |
| atomic_dec(&rdreg_usage_cnt); |
| local_irq_restore(flags); |
| } |
| |
| |
| /* |
| * read astoria register via pNAND interface |
| */ |
| static inline u16 ast_p_nand_casdo_read(u8 reg_addr8) |
| { |
| u16 data; |
| u16 addr16; |
| unsigned long flags; |
| /* |
| * throw an error if called from multiple threads |
| */ |
| static atomic_t wrreg_usage_cnt = { 0 }; |
| |
| /* |
| * disable interrupts |
| */ |
| local_irq_save(flags); |
| |
| if (atomic_read(&wrreg_usage_cnt) != 0) { |
| /* |
| * if it gets here ( from other threads), this function needs |
| * need spin_lock_irq save() protection |
| */ |
| cy_as_hal_print_message(KERN_ERR"cy_as_omap_hal: " |
| "cy_as_hal_write_register usage:%d\n", |
| atomic_read(&wrreg_usage_cnt)); |
| } |
| atomic_inc(&wrreg_usage_cnt); |
| |
| /* |
| * 2 flavors of GPMC -> PNAND access |
| */ |
| if (pnand_16bit) { |
| /* |
| * 16 BIT gpmc NAND mode |
| * CMD1, CA1, CA2, |
| */ |
| |
| IOWR8(ncmd_reg_vma, 0x05); |
| IOWR8(naddr_reg_vma, reg_addr8); |
| IOWR8(naddr_reg_vma, 0x0c); |
| IOWR8(ncmd_reg_vma, 0x00E0); |
| |
| udelay(1); |
| |
| /* |
| * much faster through the gPMC Register space |
| */ |
| data = IORD16(ndata_reg_vma); |
| } else { |
| /* |
| * 8 BIT gpmc NAND mode |
| * CMD1, CA1, CA2, CMD2 |
| */ |
| addr16 = 0x0c00 | reg_addr8; |
| IOWR8(ncmd_reg_vma, 0x05); |
| IOWR16(naddr_reg_vma, addr16); |
| IOWR8(ncmd_reg_vma, 0xE0); |
| udelay(1); |
| data = IORD16(ndata_reg_vma); |
| } |
| |
| /* |
| * re-enable interrupts |
| */ |
| atomic_dec(&wrreg_usage_cnt); |
| local_irq_restore(flags); |
| |
| return data; |
| } |
| |
| |
| /* |
| * This function must be defined to write a register within the WestBridge |
| * device. The addr value is the address of the register to write with |
| * respect to the base address of the WestBridge device. |
| */ |
| void cy_as_hal_write_register( |
| cy_as_hal_device_tag tag, |
| uint16_t addr, uint16_t data) |
| { |
| ast_p_nand_casdi_write((u8)addr, data); |
| } |
| |
| /* |
| * This function must be defined to read a register from the WestBridge |
| * device. The addr value is the address of the register to read with |
| * respect to the base address of the WestBridge device. |
| */ |
| uint16_t cy_as_hal_read_register(cy_as_hal_device_tag tag, uint16_t addr) |
| { |
| uint16_t data = 0; |
| |
| /* |
| * READ ASTORIA REGISTER USING CASDO |
| */ |
| data = ast_p_nand_casdo_read((u8)addr); |
| |
| return data; |
| } |
| |
| /* |
| * preps Ep pointers & data counters for next packet |
| * (fragment of the request) xfer returns true if |
| * there is a next transfer, and false if all bytes in |
| * current request have been xfered |
| */ |
| static inline bool prep_for_next_xfer(cy_as_hal_device_tag tag, uint8_t ep) |
| { |
| |
| if (!end_points[ep].sg_list_enabled) { |
| /* |
| * no further transfers for non storage EPs |
| * (like EP2 during firmware download, done |
| * in 64 byte chunks) |
| */ |
| if (end_points[ep].req_xfer_cnt >= end_points[ep].req_length) { |
| DBGPRN("<1> %s():RQ sz:%d non-_sg EP:%d completed\n", |
| __func__, end_points[ep].req_length, ep); |
| |
| /* |
| * no more transfers, we are done with the request |
| */ |
| return false; |
| } |
| |
| /* |
| * calculate size of the next DMA xfer, corner |
| * case for non-storage EPs where transfer size |
| * is not egual N * HAL_DMA_PKT_SZ xfers |
| */ |
| if ((end_points[ep].req_length - end_points[ep].req_xfer_cnt) |
| >= HAL_DMA_PKT_SZ) { |
| end_points[ep].dma_xfer_sz = HAL_DMA_PKT_SZ; |
| } else { |
| /* |
| * that would be the last chunk less |
| * than P-port max size |
| */ |
| end_points[ep].dma_xfer_sz = end_points[ep].req_length - |
| end_points[ep].req_xfer_cnt; |
| } |
| |
| return true; |
| } |
| |
| /* |
| * for SG_list assisted dma xfers |
| * are we done with current SG ? |
| */ |
| if (end_points[ep].seg_xfer_cnt == end_points[ep].sg_p->length) { |
| /* |
| * was it the Last SG segment on the list ? |
| */ |
| if (sg_is_last(end_points[ep].sg_p)) { |
| DBGPRN("<1> %s: EP:%d completed," |
| "%d bytes xfered\n", |
| __func__, |
| ep, |
| end_points[ep].req_xfer_cnt |
| ); |
| |
| return false; |
| } else { |
| /* |
| * There are more SG segments in current |
| * request's sg list setup new segment |
| */ |
| |
| end_points[ep].seg_xfer_cnt = 0; |
| end_points[ep].sg_p = sg_next(end_points[ep].sg_p); |
| /* set data pointer for next DMA sg transfer*/ |
| end_points[ep].data_p = sg_virt(end_points[ep].sg_p); |
| DBGPRN("<1> %s new SG:_va:%p\n\n", |
| __func__, end_points[ep].data_p); |
| } |
| |
| } |
| |
| /* |
| * for sg list xfers it will always be 512 or 1024 |
| */ |
| end_points[ep].dma_xfer_sz = HAL_DMA_PKT_SZ; |
| |
| /* |
| * next transfer is required |
| */ |
| |
| return true; |
| } |
| |
| /* |
| * Astoria DMA read request, APP_CPU reads from WB ep buffer |
| */ |
| static void cy_service_e_p_dma_read_request( |
| cy_as_omap_dev_kernel *dev_p, uint8_t ep) |
| { |
| cy_as_hal_device_tag tag = (cy_as_hal_device_tag)dev_p; |
| uint16_t v, size; |
| void *dptr; |
| uint16_t col_addr = 0x0000; |
| uint32_t row_addr = CYAS_DEV_CALC_EP_ADDR(ep); |
| uint16_t ep_dma_reg = CY_AS_MEM_P0_EP2_DMA_REG + ep - 2; |
| |
| /* |
| * get the XFER size frtom WB eP DMA REGISTER |
| */ |
| v = cy_as_hal_read_register(tag, ep_dma_reg); |
| |
| /* |
| * amount of data in EP buff in bytes |
| */ |
| size = v & CY_AS_MEM_P0_E_pn_DMA_REG_COUNT_MASK; |
| |
| /* |
| * memory pointer for this DMA packet xfer (sub_segment) |
| */ |
| dptr = end_points[ep].data_p; |
| |
| DBGPRN("<1>HAL:_svc_dma_read on EP_%d sz:%d, intr_seq:%d, dptr:%p\n", |
| ep, |
| size, |
| intr_sequence_num, |
| dptr |
| ); |
| |
| cy_as_hal_assert(size != 0); |
| |
| if (size) { |
| /* |
| * the actual WB-->OMAP memory "soft" DMA xfer |
| */ |
| p_nand_lbd_read(col_addr, row_addr, size, dptr); |
| } |
| |
| /* |
| * clear DMAVALID bit indicating that the data has been read |
| */ |
| cy_as_hal_write_register(tag, ep_dma_reg, 0); |
| |
| end_points[ep].seg_xfer_cnt += size; |
| end_points[ep].req_xfer_cnt += size; |
| |
| /* |
| * pre-advance data pointer (if it's outside sg |
| * list it will be reset anyway |
| */ |
| end_points[ep].data_p += size; |
| |
| if (prep_for_next_xfer(tag, ep)) { |
| /* |
| * we have more data to read in this request, |
| * setup next dma packet due tell WB how much |
| * data we are going to xfer next |
| */ |
| v = end_points[ep].dma_xfer_sz/*HAL_DMA_PKT_SZ*/ | |
| CY_AS_MEM_P0_E_pn_DMA_REG_DMAVAL; |
| cy_as_hal_write_register(tag, ep_dma_reg, v); |
| } else { |
| end_points[ep].pending = cy_false; |
| end_points[ep].type = cy_as_hal_none; |
| end_points[ep].buffer_valid = cy_false; |
| |
| /* |
| * notify the API that we are done with rq on this EP |
| */ |
| if (callback) { |
| DBGPRN("<1>trigg rd_dma completion cb: xfer_sz:%d\n", |
| end_points[ep].req_xfer_cnt); |
| callback(tag, ep, |
| end_points[ep].req_xfer_cnt, |
| CY_AS_ERROR_SUCCESS); |
| } |
| } |
| } |
| |
| /* |
| * omap_cpu needs to transfer data to ASTORIA EP buffer |
| */ |
| static void cy_service_e_p_dma_write_request( |
| cy_as_omap_dev_kernel *dev_p, uint8_t ep) |
| { |
| uint16_t addr; |
| uint16_t v = 0; |
| uint32_t size; |
| uint16_t col_addr = 0x0000; |
| uint32_t row_addr = CYAS_DEV_CALC_EP_ADDR(ep); |
| void *dptr; |
| |
| cy_as_hal_device_tag tag = (cy_as_hal_device_tag)dev_p; |
| /* |
| * note: size here its the size of the dma transfer could be |
| * anything > 0 && < P_PORT packet size |
| */ |
| size = end_points[ep].dma_xfer_sz; |
| dptr = end_points[ep].data_p; |
| |
| /* |
| * perform the soft DMA transfer, soft in this case |
| */ |
| if (size) |
| p_nand_lbd_write(col_addr, row_addr, size, dptr); |
| |
| end_points[ep].seg_xfer_cnt += size; |
| end_points[ep].req_xfer_cnt += size; |
| /* |
| * pre-advance data pointer |
| * (if it's outside sg list it will be reset anyway) |
| */ |
| end_points[ep].data_p += size; |
| |
| /* |
| * now clear DMAVAL bit to indicate we are done |
| * transferring data and that the data can now be |
| * sent via USB to the USB host, sent to storage, |
| * or used internally. |
| */ |
| |
| addr = CY_AS_MEM_P0_EP2_DMA_REG + ep - 2; |
| cy_as_hal_write_register(tag, addr, size); |
| |
| /* |
| * finally, tell the USB subsystem that the |
| * data is gone and we can accept the |
| * next request if one exists. |
| */ |
| if (prep_for_next_xfer(tag, ep)) { |
| /* |
| * There is more data to go. Re-init the WestBridge DMA side |
| */ |
| v = end_points[ep].dma_xfer_sz | |
| CY_AS_MEM_P0_E_pn_DMA_REG_DMAVAL; |
| cy_as_hal_write_register(tag, addr, v); |
| } else { |
| |
| end_points[ep].pending = cy_false; |
| end_points[ep].type = cy_as_hal_none; |
| end_points[ep].buffer_valid = cy_false; |
| |
| /* |
| * notify the API that we are done with rq on this EP |
| */ |
| if (callback) { |
| /* |
| * this callback will wake up the process that might be |
| * sleeping on the EP which data is being transferred |
| */ |
| callback(tag, ep, |
| end_points[ep].req_xfer_cnt, |
| CY_AS_ERROR_SUCCESS); |
| } |
| } |
| } |
| |
| /* |
| * HANDLE DRQINT from Astoria (called in AS_Intr context |
| */ |
| static void cy_handle_d_r_q_interrupt(cy_as_omap_dev_kernel *dev_p) |
| { |
| uint16_t v; |
| static uint8_t service_ep = 2; |
| |
| /* |
| * We've got DRQ INT, read DRQ STATUS Register */ |
| v = cy_as_hal_read_register((cy_as_hal_device_tag)dev_p, |
| CY_AS_MEM_P0_DRQ); |
| |
| if (v == 0) { |
| #ifndef WESTBRIDGE_NDEBUG |
| cy_as_hal_print_message("stray DRQ interrupt detected\n"); |
| #endif |
| return; |
| } |
| |
| /* |
| * Now, pick a given DMA request to handle, for now, we just |
| * go round robin. Each bit position in the service_mask |
| * represents an endpoint from EP2 to EP15. We rotate through |
| * each of the endpoints to find one that needs to be serviced. |
| */ |
| while ((v & (1 << service_ep)) == 0) { |
| |
| if (service_ep == 15) |
| service_ep = 2; |
| else |
| service_ep++; |
| } |
| |
| if (end_points[service_ep].type == cy_as_hal_write) { |
| /* |
| * handle DMA WRITE REQUEST: app_cpu will |
| * write data into astoria EP buffer |
| */ |
| cy_service_e_p_dma_write_request(dev_p, service_ep); |
| } else if (end_points[service_ep].type == cy_as_hal_read) { |
| /* |
| * handle DMA READ REQUEST: cpu will |
| * read EP buffer from Astoria |
| */ |
| cy_service_e_p_dma_read_request(dev_p, service_ep); |
| } |
| #ifndef WESTBRIDGE_NDEBUG |
| else |
| cy_as_hal_print_message("cyashalomap:interrupt," |
| " w/o pending DMA job," |
| "-check DRQ_MASK logic\n"); |
| #endif |
| |
| /* |
| * Now bump the EP ahead, so other endpoints get |
| * a shot before the one we just serviced |
| */ |
| if (end_points[service_ep].type == cy_as_hal_none) { |
| if (service_ep == 15) |
| service_ep = 2; |
| else |
| service_ep++; |
| } |
| |
| } |
| |
| void cy_as_hal_dma_cancel_request(cy_as_hal_device_tag tag, uint8_t ep) |
| { |
| DBGPRN("cy_as_hal_dma_cancel_request on ep:%d", ep); |
| if (end_points[ep].pending) |
| cy_as_hal_write_register(tag, |
| CY_AS_MEM_P0_EP2_DMA_REG + ep - 2, 0); |
| |
| end_points[ep].buffer_valid = cy_false; |
| end_points[ep].type = cy_as_hal_none; |
| } |
| |
| /* |
| * enables/disables SG list assisted DMA xfers for the given EP |
| * sg_list assisted XFERS can use physical addresses of mem pages in case if the |
| * xfer is performed by a h/w DMA controller rather then the CPU on P port |
| */ |
| void cy_as_hal_set_ep_dma_mode(uint8_t ep, bool sg_xfer_enabled) |
| { |
| end_points[ep].sg_list_enabled = sg_xfer_enabled; |
| DBGPRN("<1> EP:%d sg_list assisted DMA mode set to = %d\n", |
| ep, end_points[ep].sg_list_enabled); |
| } |
| EXPORT_SYMBOL(cy_as_hal_set_ep_dma_mode); |
| |
| /* |
| * This function must be defined to transfer a block of data to |
| * the WestBridge device. This function can use the burst write |
| * (DMA) capabilities of WestBridge to do this, or it can just copy |
| * the data using writes. |
| */ |
| void cy_as_hal_dma_setup_write(cy_as_hal_device_tag tag, |
| uint8_t ep, void *buf, |
| uint32_t size, uint16_t maxsize) |
| { |
| uint32_t addr = 0; |
| uint16_t v = 0; |
| |
| /* |
| * Note: "size" is the actual request size |
| * "maxsize" - is the P port fragment size |
| * No EP0 or EP1 traffic should get here |
| */ |
| cy_as_hal_assert(ep != 0 && ep != 1); |
| |
| /* |
| * If this asserts, we have an ordering problem. Another DMA request |
| * is coming down before the previous one has completed. |
| */ |
| cy_as_hal_assert(end_points[ep].buffer_valid == cy_false); |
| end_points[ep].buffer_valid = cy_true; |
| end_points[ep].type = cy_as_hal_write; |
| end_points[ep].pending = cy_true; |
| |
| /* |
| * total length of the request |
| */ |
| end_points[ep].req_length = size; |
| |
| if (size >= maxsize) { |
| /* |
| * set xfer size for very 1st DMA xfer operation |
| * port max packet size ( typically 512 or 1024) |
| */ |
| end_points[ep].dma_xfer_sz = maxsize; |
| } else { |
| /* |
| * smaller xfers for non-storage EPs |
| */ |
| end_points[ep].dma_xfer_sz = size; |
| } |
| |
| /* |
| * check the EP transfer mode uses sg_list rather then a memory buffer |
| * block devices pass it to the HAL, so the hAL could get to the real |
| * physical address for each segment and set up a DMA controller |
| * hardware ( if there is one) |
| */ |
| if (end_points[ep].sg_list_enabled) { |
| /* |
| * buf - pointer to the SG list |
| * data_p - data pointer to the 1st DMA segment |
| * seg_xfer_cnt - keeps track of N of bytes sent in current |
| * sg_list segment |
| * req_xfer_cnt - keeps track of the total N of bytes |
| * transferred for the request |
| */ |
| end_points[ep].sg_p = buf; |
| end_points[ep].data_p = sg_virt(end_points[ep].sg_p); |
| end_points[ep].seg_xfer_cnt = 0; |
| end_points[ep].req_xfer_cnt = 0; |
| |
| #ifdef DBGPRN_DMA_SETUP_WR |
| DBGPRN("cyasomaphal:%s: EP:%d, buf:%p, buf_va:%p," |
| "req_sz:%d, maxsz:%d\n", |
| __func__, |
| ep, |
| buf, |
| end_points[ep].data_p, |
| size, |
| maxsize); |
| #endif |
| |
| } else { |
| /* |
| * setup XFER for non sg_list assisted EPs |
| */ |
| |
| #ifdef DBGPRN_DMA_SETUP_WR |
| DBGPRN("<1>%s non storage or sz < 512:" |
| "EP:%d, sz:%d\n", __func__, ep, size); |
| #endif |
| |
| end_points[ep].sg_p = NULL; |
| |
| /* |
| * must be a VMA of a membuf in kernel space |
| */ |
| end_points[ep].data_p = buf; |
| |
| /* |
| * will keep track No of bytes xferred for the request |
| */ |
| end_points[ep].req_xfer_cnt = 0; |
| } |
| |
| /* |
| * Tell WB we are ready to send data on the given endpoint |
| */ |
| v = (end_points[ep].dma_xfer_sz & CY_AS_MEM_P0_E_pn_DMA_REG_COUNT_MASK) |
| | CY_AS_MEM_P0_E_pn_DMA_REG_DMAVAL; |
| |
| addr = CY_AS_MEM_P0_EP2_DMA_REG + ep - 2; |
| |
| cy_as_hal_write_register(tag, addr, v); |
| } |
| |
| /* |
| * This function must be defined to transfer a block of data from |
| * the WestBridge device. This function can use the burst read |
| * (DMA) capabilities of WestBridge to do this, or it can just |
| * copy the data using reads. |
| */ |
| void cy_as_hal_dma_setup_read(cy_as_hal_device_tag tag, |
| uint8_t ep, void *buf, |
| uint32_t size, uint16_t maxsize) |
| { |
| uint32_t addr; |
| uint16_t v; |
| |
| /* |
| * Note: "size" is the actual request size |
| * "maxsize" - is the P port fragment size |
| * No EP0 or EP1 traffic should get here |
| */ |
| cy_as_hal_assert(ep != 0 && ep != 1); |
| |
| /* |
| * If this asserts, we have an ordering problem. |
| * Another DMA request is coming down before the |
| * previous one has completed. we should not get |
| * new requests if current is still in process |
| */ |
| |
| cy_as_hal_assert(end_points[ep].buffer_valid == cy_false); |
| |
| end_points[ep].buffer_valid = cy_true; |
| end_points[ep].type = cy_as_hal_read; |
| end_points[ep].pending = cy_true; |
| end_points[ep].req_xfer_cnt = 0; |
| end_points[ep].req_length = size; |
| |
| if (size >= maxsize) { |
| /* |
| * set xfer size for very 1st DMA xfer operation |
| * port max packet size ( typically 512 or 1024) |
| */ |
| end_points[ep].dma_xfer_sz = maxsize; |
| } else { |
| /* |
| * so that we could handle small xfers on in case |
| * of non-storage EPs |
| */ |
| end_points[ep].dma_xfer_sz = size; |
| } |
| |
| addr = CY_AS_MEM_P0_EP2_DMA_REG + ep - 2; |
| |
| if (end_points[ep].sg_list_enabled) { |
| /* |
| * Handle sg-list assisted EPs |
| * seg_xfer_cnt - keeps track of N of sent packets |
| * buf - pointer to the SG list |
| * data_p - data pointer for the 1st DMA segment |
| */ |
| end_points[ep].seg_xfer_cnt = 0; |
| end_points[ep].sg_p = buf; |
| end_points[ep].data_p = sg_virt(end_points[ep].sg_p); |
| |
| #ifdef DBGPRN_DMA_SETUP_RD |
| DBGPRN("cyasomaphal:DMA_setup_read sg_list EP:%d, " |
| "buf:%p, buf_va:%p, req_sz:%d, maxsz:%d\n", |
| ep, |
| buf, |
| end_points[ep].data_p, |
| size, |
| maxsize); |
| #endif |
| v = (end_points[ep].dma_xfer_sz & |
| CY_AS_MEM_P0_E_pn_DMA_REG_COUNT_MASK) | |
| CY_AS_MEM_P0_E_pn_DMA_REG_DMAVAL; |
| cy_as_hal_write_register(tag, addr, v); |
| } else { |
| /* |
| * Non sg list EP passed void *buf rather then scatterlist *sg |
| */ |
| #ifdef DBGPRN_DMA_SETUP_RD |
| DBGPRN("%s:non-sg_list EP:%d," |
| "RQ_sz:%d, maxsz:%d\n", |
| __func__, ep, size, maxsize); |
| #endif |
| |
| end_points[ep].sg_p = NULL; |
| |
| /* |
| * must be a VMA of a membuf in kernel space |
| */ |
| end_points[ep].data_p = buf; |
| |
| /* |
| * Program the EP DMA register for Storage endpoints only. |
| */ |
| if (is_storage_e_p(ep)) { |
| v = (end_points[ep].dma_xfer_sz & |
| CY_AS_MEM_P0_E_pn_DMA_REG_COUNT_MASK) | |
| CY_AS_MEM_P0_E_pn_DMA_REG_DMAVAL; |
| cy_as_hal_write_register(tag, addr, v); |
| } |
| } |
| } |
| |
| /* |
| * This function must be defined to allow the WB API to |
| * register a callback function that is called when a |
| * DMA transfer is complete. |
| */ |
| void cy_as_hal_dma_register_callback(cy_as_hal_device_tag tag, |
| cy_as_hal_dma_complete_callback cb) |
| { |
| DBGPRN("<1>\n%s: WB API has registered a dma_complete callback:%x\n", |
| __func__, (uint32_t)cb); |
| callback = cb; |
| } |
| |
| /* |
| * This function must be defined to return the maximum size of |
| * DMA request that can be handled on the given endpoint. The |
| * return value should be the maximum size in bytes that the DMA |
| * module can handle. |
| */ |
| uint32_t cy_as_hal_dma_max_request_size(cy_as_hal_device_tag tag, |
| cy_as_end_point_number_t ep) |
| { |
| /* |
| * Storage reads and writes are always done in 512 byte blocks. |
| * So, we do the count handling within the HAL, and save on |
| * some of the data transfer delay. |
| */ |
| if ((ep == CYASSTORAGE_READ_EP_NUM) || |
| (ep == CYASSTORAGE_WRITE_EP_NUM)) { |
| /* max DMA request size HAL can handle by itself */ |
| return CYASSTORAGE_MAX_XFER_SIZE; |
| } else { |
| /* |
| * For the USB - Processor endpoints, the maximum transfer |
| * size depends on the speed of USB operation. So, we use |
| * the following constant to indicate to the API that |
| * splitting of the data into chunks less that or equal to |
| * the max transfer size should be handled internally. |
| */ |
| |
| /* DEFINED AS 0xffffffff in cyasdma.h */ |
| return CY_AS_DMA_MAX_SIZE_HW_SIZE; |
| } |
| } |
| |
| /* |
| * This function must be defined to set the state of the WAKEUP pin |
| * on the WestBridge device. Generally this is done via a GPIO of |
| * some type. |
| */ |
| cy_bool cy_as_hal_set_wakeup_pin(cy_as_hal_device_tag tag, cy_bool state) |
| { |
| /* |
| * Not supported as of now. |
| */ |
| return cy_false; |
| } |
| |
| void cy_as_hal_pll_lock_loss_handler(cy_as_hal_device_tag tag) |
| { |
| cy_as_hal_print_message("error: astoria PLL lock is lost\n"); |
| cy_as_hal_print_message("please check the input voltage levels"); |
| cy_as_hal_print_message("and clock, and restart the system\n"); |
| } |
| |
| /* |
| * Below are the functions that must be defined to provide the basic |
| * operating system services required by the API. |
| */ |
| |
| /* |
| * This function is required by the API to allocate memory. |
| * This function is expected to work exactly like malloc(). |
| */ |
| void *cy_as_hal_alloc(uint32_t cnt) |
| { |
| return kmalloc(cnt, GFP_ATOMIC); |
| } |
| |
| /* |
| * This function is required by the API to free memory allocated |
| * with CyAsHalAlloc(). This function is'expected to work exacly |
| * like free(). |
| */ |
| void cy_as_hal_free(void *mem_p) |
| { |
| kfree(mem_p); |
| } |
| |
| /* |
| * Allocator that can be used in interrupt context. |
| * We have to ensure that the kmalloc call does not |
| * sleep in this case. |
| */ |
| void *cy_as_hal_c_b_alloc(uint32_t cnt) |
| { |
| return kmalloc(cnt, GFP_ATOMIC); |
| } |
| |
| /* |
| * This function is required to set a block of memory to a |
| * specific value. This function is expected to work exactly |
| * like memset() |
| */ |
| void cy_as_hal_mem_set(void *ptr, uint8_t value, uint32_t cnt) |
| { |
| memset(ptr, value, cnt); |
| } |
| |
| /* |
| * This function is expected to create a sleep channel. |
| * The data structure that represents the sleep channel object |
| * sleep channel (which is Linux "wait_queue_head_t wq" for this particular HAL) |
| * passed as a pointer, and allpocated by the caller |
| * (typically as a local var on the stack) "Create" word should read as |
| * "SleepOn", this func doesn't actually create anything |
| */ |
| cy_bool cy_as_hal_create_sleep_channel(cy_as_hal_sleep_channel *channel) |
| { |
| init_waitqueue_head(&channel->wq); |
| return cy_true; |
| } |
| |
| /* |
| * for this particular HAL it doesn't actually destroy anything |
| * since no actual sleep object is created in CreateSleepChannel() |
| * sleep channel is given by the pointer in the argument. |
| */ |
| cy_bool cy_as_hal_destroy_sleep_channel(cy_as_hal_sleep_channel *channel) |
| { |
| return cy_true; |
| } |
| |
| /* |
| * platform specific wakeable Sleep implementation |
| */ |
| cy_bool cy_as_hal_sleep_on(cy_as_hal_sleep_channel *channel, uint32_t ms) |
| { |
| wait_event_interruptible_timeout(channel->wq, 0, ((ms * HZ)/1000)); |
| return cy_true; |
| } |
| |
| /* |
| * wakes up the process waiting on the CHANNEL |
| */ |
| cy_bool cy_as_hal_wake(cy_as_hal_sleep_channel *channel) |
| { |
| wake_up_interruptible_all(&channel->wq); |
| return cy_true; |
| } |
| |
| uint32_t cy_as_hal_disable_interrupts() |
| { |
| if (0 == intr__enable) |
| ; |
| |
| intr__enable++; |
| return 0; |
| } |
| |
| void cy_as_hal_enable_interrupts(uint32_t val) |
| { |
| intr__enable--; |
| if (0 == intr__enable) |
| ; |
| } |
| |
| /* |
| * Sleep atleast 150ns, cpu dependent |
| */ |
| void cy_as_hal_sleep150(void) |
| { |
| uint32_t i, j; |
| |
| j = 0; |
| for (i = 0; i < 1000; i++) |
| j += (~i); |
| } |
| |
| void cy_as_hal_sleep(uint32_t ms) |
| { |
| cy_as_hal_sleep_channel channel; |
| |
| cy_as_hal_create_sleep_channel(&channel); |
| cy_as_hal_sleep_on(&channel, ms); |
| cy_as_hal_destroy_sleep_channel(&channel); |
| } |
| |
| cy_bool cy_as_hal_is_polling() |
| { |
| return cy_false; |
| } |
| |
| void cy_as_hal_c_b_free(void *ptr) |
| { |
| cy_as_hal_free(ptr); |
| } |
| |
| /* |
| * suppose to reinstate the astoria registers |
| * that may be clobbered in sleep mode |
| */ |
| void cy_as_hal_init_dev_registers(cy_as_hal_device_tag tag, |
| cy_bool is_standby_wakeup) |
| { |
| /* specific to SPI, no implementation required */ |
| (void) tag; |
| (void) is_standby_wakeup; |
| } |
| |
| void cy_as_hal_read_regs_before_standby(cy_as_hal_device_tag tag) |
| { |
| /* specific to SPI, no implementation required */ |
| (void) tag; |
| } |
| |
| cy_bool cy_as_hal_sync_device_clocks(cy_as_hal_device_tag tag) |
| { |
| /* |
| * we are in asynchronous mode. so no need to handle this |
| */ |
| return true; |
| } |
| |
| /* |
| * init OMAP h/w resources |
| */ |
| int start_o_m_a_p_kernel(const char *pgm, |
| cy_as_hal_device_tag *tag, cy_bool debug) |
| { |
| cy_as_omap_dev_kernel *dev_p; |
| int i; |
| u16 data16[4]; |
| u8 pncfg_reg; |
| |
| /* |
| * No debug mode support through argument as of now |
| */ |
| (void)debug; |
| |
| DBGPRN(KERN_INFO"starting OMAP34xx HAL...\n"); |
| |
| /* |
| * Initialize the HAL level endpoint DMA data. |
| */ |
| for (i = 0; i < sizeof(end_points)/sizeof(end_points[0]); i++) { |
| end_points[i].data_p = 0; |
| end_points[i].pending = cy_false; |
| end_points[i].size = 0; |
| end_points[i].type = cy_as_hal_none; |
| end_points[i].sg_list_enabled = cy_false; |
| |
| /* |
| * by default the DMA transfers to/from the E_ps don't |
| * use sg_list that implies that the upper devices like |
| * blockdevice have to enable it for the E_ps in their |
| * initialization code |
| */ |
| } |
| |
| /* |
| * allocate memory for OMAP HAL |
| */ |
| dev_p = (cy_as_omap_dev_kernel *)cy_as_hal_alloc( |
| sizeof(cy_as_omap_dev_kernel)); |
| if (dev_p == 0) { |
| cy_as_hal_print_message("out of memory allocating OMAP" |
| "device structure\n"); |
| return 0; |
| } |
| |
| dev_p->m_sig = CY_AS_OMAP_KERNEL_HAL_SIG; |
| |
| /* |
| * initialize OMAP hardware and StartOMAPKernelall gpio pins |
| */ |
| dev_p->m_addr_base = (void *)cy_as_hal_processor_hw_init(); |
| |
| /* |
| * Now perform a hard reset of the device to have |
| * the new settings take effect |
| */ |
| __gpio_set_value(AST_WAKEUP, 1); |
| |
| /* |
| * do Astoria h/w reset |
| */ |
| DBGPRN(KERN_INFO"-_-_pulse -> westbridge RST pin\n"); |
| |
| /* |
| * NEGATIVE PULSE on RST pin |
| */ |
| __gpio_set_value(AST_RESET, 0); |
| mdelay(1); |
| __gpio_set_value(AST_RESET, 1); |
| mdelay(50); |
| |
| /* |
| * note AFTER reset PNAND interface is 8 bit mode |
| * so if gpmc Is configured in 8 bit mode upper half will be FF |
| */ |
| pncfg_reg = ast_p_nand_casdo_read(CY_AS_MEM_PNAND_CFG); |
| |
| #ifdef PNAND_16BIT_MODE |
| |
| /* |
| * switch to 16 bit mode, force NON-LNA LBD mode, 3 RA addr bytes |
| */ |
| ast_p_nand_casdi_write(CY_AS_MEM_PNAND_CFG, 0x0001); |
| |
| /* |
| * now in order to continue to talk to astoria |
| * sw OMAP GPMC into 16 bit mode as well |
| */ |
| cy_as_hal_gpmc_enable_16bit_bus(cy_true); |
| #else |
| /* Astoria and GPMC are already in 8 bit mode, just initialize PNAND_CFG */ |
| ast_p_nand_casdi_write(CY_AS_MEM_PNAND_CFG, 0x0000); |
| #endif |
| |
| /* |
| * NOTE: if you want to capture bus activity on the LA, |
| * don't use printks in between the activities you want to capture. |
| * prinks may take milliseconds, and the data of interest |
| * will fall outside the LA capture window/buffer |
| */ |
| data16[0] = ast_p_nand_casdo_read(CY_AS_MEM_CM_WB_CFG_ID); |
| data16[1] = ast_p_nand_casdo_read(CY_AS_MEM_PNAND_CFG); |
| |
| if (data16[0] != 0xA200) { |
| /* |
| * astoria device is not found |
| */ |
| printk(KERN_ERR "ERROR: astoria device is not found, CY_AS_MEM_CM_WB_CFG_ID "); |
| printk(KERN_ERR "read returned:%4.4X: CY_AS_MEM_PNAND_CFG:%4.4x !\n", |
| data16[0], data16[0]); |
| goto bus_acc_error; |
| } |
| |
| cy_as_hal_print_message(KERN_INFO" register access CASDO test:" |
| "\n CY_AS_MEM_CM_WB_CFG_ID:%4.4x\n" |
| "PNAND_CFG after RST:%4.4x\n " |
| "CY_AS_MEM_PNAND_CFG" |
| "after cfg_wr:%4.4x\n\n", |
| data16[0], pncfg_reg, data16[1]); |
| |
| dev_p->thread_flag = 1; |
| spin_lock_init(&int_lock); |
| dev_p->m_next_p = m_omap_list_p; |
| |
| m_omap_list_p = dev_p; |
| *tag = dev_p; |
| |
| cy_as_hal_configure_interrupts((void *)dev_p); |
| |
| cy_as_hal_print_message(KERN_INFO"OMAP3430__hal started tag:%p" |
| ", kernel HZ:%d\n", dev_p, HZ); |
| |
| /* |
| *make processor to storage endpoints SG assisted by default |
| */ |
| cy_as_hal_set_ep_dma_mode(4, true); |
| cy_as_hal_set_ep_dma_mode(8, true); |
| |
| return 1; |
| |
| /* |
| * there's been a NAND bus access error or |
| * astoria device is not connected |
| */ |
| bus_acc_error: |
| /* |
| * at this point hal tag hasn't been set yet |
| * so the device will not call omap_stop |
| */ |
| cy_as_hal_omap_hardware_deinit(dev_p); |
| cy_as_hal_free(dev_p); |
| return 0; |
| } |
| |
| #else |
| /* |
| * Some compilers do not like empty C files, so if the OMAP hal is not being |
| * compiled, we compile this single function. We do this so that for a |
| * given target HAL there are not multiple sources for the HAL functions. |
| */ |
| void my_o_m_a_p_kernel_hal_dummy_function(void) |
| { |
| } |
| |
| #endif |