int ocmem_rdm_init(struct platform_device *pdev) { struct ocmem_plat_data *pdata = NULL; int rc = 0; pdata = platform_get_drvdata(pdev); br_base = pdata->br_base; dm_base = pdata->dm_base; rc = devm_request_irq(&pdev->dev, pdata->dm_irq, ocmem_dm_irq_handler, IRQF_TRIGGER_RISING, "ocmem_dm_irq", pdata); if (rc) { dev_err(&pdev->dev, "Failed to request dm irq"); return -EINVAL; } rc = ocmem_enable_core_clock(); if (rc < 0) { pr_err("RDM initialization failed\n"); return rc; } init_completion(&dm_clear_event); init_completion(&dm_transfer_event); ocmem_disable_core_clock(); return 0; }
int ocmem_core_init(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ocmem_plat_data *pdata = NULL; unsigned hw_ver; bool interleaved; unsigned i, j, k; unsigned rsc_type = 0; int rc = 0; pdata = platform_get_drvdata(pdev); ocmem_base = pdata->reg_base; rc = ocmem_enable_core_clock(); if (rc < 0) return rc; hw_ver = ocmem_read(ocmem_base + OC_HW_PROFILE); if (pdata->nr_regions != OCMEM_V1_REGIONS) { pr_err("Invalid number of regions (%d)\n", pdata->nr_regions); goto hw_not_supported; } num_macros = (hw_ver & NUM_MACROS_MASK) >> NUM_MACROS_SHIFT; num_ports = (hw_ver & NUM_PORTS_MASK) >> NUM_PORTS_SHIFT; if (num_macros != OCMEM_V1_MACROS) { pr_err("Invalid number of macros (%d)\n", pdata->nr_macros); goto hw_not_supported; } interleaved = (hw_ver & INTERLEAVING_MASK) >> INTERLEAVING_SHIFT; if (interleaved == false) { pr_err("Interleaving is disabled\n"); goto hw_not_supported; } num_regions = pdata->nr_regions; pdata->interleaved = true; pdata->nr_macros = num_macros; pdata->nr_ports = num_ports; macro_size = OCMEM_V1_MACRO_SZ * 2; num_banks = num_ports / 2; region_size = macro_size * num_banks; rsc_type = pdata->rpm_rsc_type; pr_debug("ocmem_core: ports %d regions %d macros %d interleaved %d\n", num_ports, num_regions, num_macros, interleaved); region_ctrl = devm_kzalloc(dev, sizeof(struct ocmem_hw_region) * num_regions, GFP_KERNEL); if (!region_ctrl) { goto err_no_mem; } mutex_init(®ion_ctrl_lock); for (i = 0 ; i < num_regions; i++) { struct ocmem_hw_region *region = ®ion_ctrl[i]; struct msm_rpm_request *req = NULL; region->interleaved = interleaved; region->mode = MODE_DEFAULT; region->r_state = REGION_DEFAULT_OFF; region->num_macros = num_banks; region->macro = devm_kzalloc(dev, sizeof(struct ocmem_hw_macro) * num_banks, GFP_KERNEL); if (!region->macro) { goto err_no_mem; } for (j = 0; j < num_banks; j++) { struct ocmem_hw_macro *m = ®ion->macro[j]; m->m_state = MACRO_OFF; for (k = 0; k < OCMEM_CLIENT_MAX; k++) { atomic_set(&m->m_on[k], 0); atomic_set(&m->m_retain[k], 0); } } if (pdata->rpm_pwr_ctrl) { rpm_power_control = true; req = msm_rpm_create_request(MSM_RPM_CTX_ACTIVE_SET, rsc_type, i, num_banks); if (!req) { pr_err("Unable to create RPM request\n"); goto region_init_error; } pr_debug("rpm request type %x (rsc: %d) with %d elements\n", rsc_type, i, num_banks); region->rpm_req = req; } if (ocmem_region_toggle(i)) { pr_err("Failed to verify region %d\n", i); goto region_init_error; } if (ocmem_region_set_default_state(i)) { pr_err("Failed to initialize region %d\n", i); goto region_init_error; } } rc = ocmem_core_set_default_state(); if (rc < 0) return rc; ocmem_disable_core_clock(); return 0; err_no_mem: pr_err("ocmem: Unable to allocate memory\n"); region_init_error: hw_not_supported: pr_err("Unsupported OCMEM h/w configuration %x\n", hw_ver); ocmem_disable_core_clock(); return -EINVAL; }
/* Lock during transfers */ int ocmem_rdm_transfer(int id, struct ocmem_map_list *clist, unsigned long start, int direction) { int num_chunks = clist->num_chunks; int slot = client_slot_start(id); int table_start = 0; int table_end = 0; int br_ctrl = 0; int br_id = 0; int client_id = 0; int dm_ctrl = 0; int i = 0; int j = 0; int status = 0; int rc = 0; rc = ocmem_enable_core_clock(); if (rc < 0) { pr_err("RDM transfer failed for client %s (id: %d)\n", get_name(id), id); return rc; } /* Clear DM Mask */ ocmem_write(DM_MASK_RESET, dm_base + DM_INTR_MASK); /* Clear DM Interrupts */ ocmem_write(DM_INTR_RESET, dm_base + DM_INTR_CLR); for (i = 0, j = slot; i < num_chunks; i++, j++) { struct ocmem_chunk *chunk = &clist->chunks[i]; int sz = chunk->size; int paddr = chunk->ddr_paddr; int tbl_n_ctrl = 0; tbl_n_ctrl |= BR_TBL_ENTRY_ENABLE; if (chunk->ro) tbl_n_ctrl |= (1 << BR_RW_SHIFT); /* Table Entry n of BR and DM */ ocmem_write(start, br_base + BR_TBL_n_offset(j)); ocmem_write(sz, br_base + BR_TBL_n_size(j)); ocmem_write(paddr, br_base + BR_TBL_n_paddr(j)); ocmem_write(tbl_n_ctrl, br_base + BR_TBL_n_ctrl(j)); ocmem_write(start, dm_base + DM_TBL_n_offset(j)); ocmem_write(sz, dm_base + DM_TBL_n_size(j)); ocmem_write(paddr, dm_base + DM_TBL_n_paddr(j)); ocmem_write(tbl_n_ctrl, dm_base + DM_TBL_n_ctrl(j)); start += sz; } br_id = client_ctrl_id(id); table_start = slot; table_end = slot + num_chunks - 1; br_ctrl |= (table_start << BR_TBL_START); br_ctrl |= (table_end << BR_TBL_END); ocmem_write(br_ctrl, (br_base + BR_CLIENT_n_ctrl(br_id))); /* Enable BR */ ocmem_write(0x1, br_base + BR_CTRL); /* Compute DM Control Value */ dm_ctrl |= (table_start << DM_TBL_START); dm_ctrl |= (table_end << DM_TBL_END); client_id = client_ctrl_id(id); dm_ctrl |= (client_id << DM_CLIENT_SHIFT); dm_ctrl |= (DM_BR_ID_LPASS << DM_BR_ID_SHIFT); dm_ctrl |= (DM_BLOCK_256 << DM_BR_BLK_SHIFT); dm_ctrl |= (direction << DM_DIR_SHIFT); status = ocmem_read(dm_base + DM_GEN_STATUS); pr_debug("Transfer status before %x\n", status); INIT_COMPLETION(dm_transfer_event); /* The DM and BR tables must be programmed before triggering the * Data Mover else the coherent transfer would be corrupted */ mb(); /* Trigger DM */ ocmem_write(dm_ctrl, dm_base + DM_CTRL); pr_debug("ocmem: rdm: dm_ctrl %x br_ctrl %x\n", dm_ctrl, br_ctrl); wait_for_completion(&dm_transfer_event); pr_debug("Completed transferring %d segments\n", num_chunks); ocmem_disable_core_clock(); return 0; }
static int switch_power_state(int id, unsigned long offset, unsigned long len, unsigned new_state) { unsigned region_start = num_regions; unsigned region_end = num_regions; unsigned curr_state = 0x0; int i = 0; int j = 0; unsigned start_m = num_banks; unsigned end_m = num_banks; unsigned long region_offset = 0; int rc = 0; if (offset < 0) return -EINVAL; if (len < macro_size) return -EINVAL; pr_debug("ocmem: power_transition to %x for client %d\n", new_state, id); region_start = offset / region_size; region_end = (offset + len - 1) / region_size; pr_debug("ocmem: region start %u end %u\n", region_start, region_end); if (region_start >= num_regions || (region_end >= num_regions)) return -EINVAL; rc = ocmem_enable_core_clock(); if (rc < 0) { pr_err("ocmem: Power transistion request for client %s (id: %d) failed\n", get_name(id), id); return rc; } mutex_lock(®ion_ctrl_lock); for (i = region_start; i <= region_end; i++) { curr_state = read_region_state(i); switch (curr_state) { case REGION_DEFAULT_OFF: if (new_state != REGION_DEFAULT_ON) goto invalid_transition; break; case REGION_DEFAULT_RETENTION: if (new_state != REGION_DEFAULT_ON) goto invalid_transition; break; default: break; } if (len >= region_size) { pr_debug("switch: entire region (%d)\n", i); start_m = 0; end_m = num_banks; } else { region_offset = offset - (i * region_size); start_m = region_offset / macro_size; end_m = (region_offset + len - 1) / macro_size; pr_debug("switch: macro (%u to %u)\n", start_m, end_m); } for (j = start_m; j <= end_m; j++) { pr_debug("vote: macro (%d) region (%d)\n", j, i); apply_macro_vote(id, i, j, hw_macro_state(new_state)); aggregate_macro_state(i, j); } aggregate_region_state(i); commit_region_state(i); len -= region_size; /* If we voted ON/retain the banks must never be OFF */ if (new_state != REGION_DEFAULT_OFF) { if (memory_is_off(i)) { pr_err("ocmem: Accessing memory during sleep\n"); WARN_ON(1); } } } mutex_unlock(®ion_ctrl_lock); ocmem_disable_core_clock(); return 0; invalid_transition: mutex_unlock(®ion_ctrl_lock); ocmem_disable_core_clock(); pr_err("ocmem_core: Invalid state transition detected for %d\n", id); pr_err("ocmem_core: Offset %lx Len %lx curr_state %x new_state %x\n", offset, len, curr_state, new_state); WARN_ON(1); return -EINVAL; }