blob: a6780296888f1114c04e0bde4bbfbdbc2978d77a [file] [log] [blame]
/* 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 upto 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))
/*
* persistant, stores current GPMC interface cfg mode
*/
static uint8_t pnand_16bit;
/*
* keep processing new WB DRQ in ISR untill 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 transfered 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;
/*
* get GPMC i/o registers base(already been i/o mapped
* in kernel, no need for separate i/o remap)
*/
gpmc_base = phys_to_virt(OMAP34XX_GPMC_BASE);
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;
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)
{
void *ret_p;
ret_p = kmalloc(cnt, GFP_ATOMIC);
return ret_p;
}
/*
* 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)
{
void *ret_p;
ret_p = kmalloc(cnt, GFP_ATOMIC);
return ret_p;
}
/*
* 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 paticular 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, jsut 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