static unsigned bcm_mpi_get_clk_cfg(u32 hz, u8 cs_off_clk_cycles) { u8 clk_cfg = 0; size_t freq_idx; dd_bcm_pr_debug("%s(hz=%lu, cs_off_clk_cycles=%u)\n", __func__, (unsigned long)(hz), (unsigned int)(cs_off_clk_cycles)); /* Find the closest clock configuration */ for (freq_idx = 0; (freq_idx < ARRAY_SIZE(bcm63xx_spi_freq_table)); freq_idx += 1) { if (hz >= bcm63xx_spi_freq_table[freq_idx][0]) { clk_cfg |= bcm63xx_spi_freq_table[freq_idx][1]; break; } } /* No matching configuration found, default to lowest */ if (freq_idx >= ARRAY_SIZE(bcm63xx_spi_freq_table)) { clk_cfg |= SPI_CLK_0_391MHZ; } bcm_assert(((cs_off_clk_cycles << SPI_SSOFFTIME_SHIFT) & (~(SPI_SSOFFTIME_MASK))) == 0); clk_cfg |= (cs_off_clk_cycles << SPI_SSOFFTIME_SHIFT); return (clk_cfg); }
int bcm_mpi_read_write(bcm_mpi_t *t, __u8 *buf, __u8 buf_len) { int ret = 0; #ifdef BCMPH_USE_SPI_DRIVER # ifndef BCMPH_NOHW struct spi_transfer tr; struct spi_message msg; # endif // !BCMPH_NOHW dd_bcm_pr_debug("%s(buf_len=%d)\n", __func__, (int)(buf_len)); # ifndef BCMPH_NOHW # ifdef BCMPH_DEBUG_MPI bcm_mpi_add_trace(t, MPI_WRITE, buf, buf_len); # endif // BCMPH_DEBUG_MPI spi_message_init(&(msg)); memset(&(tr), 0, sizeof(tr)); tr.tx_buf = buf; tr.rx_buf = buf; tr.len = buf_len; tr.bits_per_word = 8; tr.speed_hz = t->mpi_clk; tr.cs_change = 0; spi_message_add_tail(&(tr), &(msg)); ret = bcm_make_transfer(t, &(msg)); # ifdef BCMPH_DEBUG_MPI bcm_mpi_add_trace(t, MPI_READ, buf, ret); # endif // BCMPH_DEBUG_MPI # endif // !BCMPH_NOHW #else // !BCMPH_USE_SPI_DRIVER dd_bcm_pr_debug("%s(buf_len=%d)\n", __func__, (int)(buf_len)); # ifndef BCMPH_NOHW bcm_assert(buf_len <= t->dev_data->fifo_size); if (buf_len > 0) { # ifdef BCMPH_DEBUG_MPI bcm_mpi_add_trace(t, MPI_WRITE, buf, buf_len); # endif // BCMPH_DEBUG_MPI bcm_mpi_setup_transfer(t); ret = bcm_mpi_rw_buf(t, buf, buf_len, true, true, NULL, 0); # ifdef BCMPH_DEBUG_MPI bcm_mpi_add_trace(t, MPI_READ, buf, ret); # endif // BCMPH_DEBUG_MPI } else { ret = 0; } # endif // !BCMPH_NOHW #endif // !BCMPH_USE_SPI_DRIVER return (ret); }
static void bcm_timer_compute_params(bcm_timer_t *t, unsigned long period_in_usecs) { unsigned long real_period; bcm_pr_debug("bcm_timer_compute_params(period_in_usecs=%lu)\n", (unsigned long)(period_in_usecs)); t->period_in_jiffies = usecs_to_jiffies(period_in_usecs); real_period = jiffies_to_usecs(t->period_in_jiffies); if (real_period < period_in_usecs) { t->period_in_jiffies += 1; real_period = jiffies_to_usecs(t->period_in_jiffies); bcm_assert(real_period >= period_in_usecs); } t->drift_increment = real_period - period_in_usecs; bcm_assert((0 == t->drift_increment) || ((t->drift_increment > 0) && (t->period_in_jiffies >= 1))); bcm_pr_debug("one_jiffie_to_usecs = %lu, period_in_jiffies = %lu, real_period = %lu, drift_increment = %lu\n", (unsigned long)(one_jiffie_to_usecs), (unsigned long)(t->period_in_jiffies), (unsigned long)(real_period), (unsigned long)(t->drift_increment)); }
int __init phone_dev_le88266_init(phone_dev_le88266_t *t, const phone_desc_device_t *dev_desc, __u8 tick_period) { int ret = 0; size_t line_idx; bcm_pr_debug("%s()\n", __func__); bcm_assert(ARRAY_SIZE(t->lines) <= ARRAY_SIZE(t->ve880.vdz.lines)); bcm_assert((NULL != dev_desc) && (NULL != dev_desc->parameters.zarlink)); for (line_idx = 0; (line_idx < dev_desc->line_count); line_idx += 1) { bcm_assert(NULL != dev_desc->lines[line_idx].parameters.zarlink); } do { // Empty loop if (dev_desc->line_count > ARRAY_SIZE(t->lines)) { bcm_pr_err("Le88266 description can only have %lu lines at most\n", (unsigned long)(ARRAY_SIZE(t->lines))); ret = -ENODEV; break; } ret = phone_dev_zarlink_ve880_init(&(t->ve880), &(vtbl_le88266), dev_desc, tick_period); if (ret) { break; } for (line_idx = 0; (line_idx < ARRAY_SIZE(t->lines)); line_idx += 1) { phone_line_init(&(t->lines[line_idx].vl)); memset(&(t->lines[line_idx].obj), 0, sizeof(t->lines[line_idx].obj)); memset(&(t->lines[line_idx].ctx), 0, sizeof(t->lines[line_idx].ctx)); phone_dev_zarlink_init_line(&(t->ve880.vdz), line_idx, &(t->lines[line_idx].vl), &(t->lines[line_idx].obj), &(t->lines[line_idx].ctx)); } } while (false); return (ret); }
int __init bcm_timer_init(bcm_timer_t *t, void (*callback)(bcm_timer_t *t)) { int ret = 0; bcm_pr_debug("bcm_timer_init()\n"); bcm_assert(NULL != callback); one_jiffie_to_usecs = jiffies_to_usecs(1); t->callback = callback; t->period_in_jiffies = msecs_to_jiffies(1000); // Init kernel timer init_timer(&(t->kobject)); t->kobject.function = bcm_timer_fn; t->kobject.data = (unsigned long)(t); return (ret); }
static int bcm63xx_spi_remove(struct platform_device *pdev) { bcm_mpi_dev_data_t *bs = platform_get_drvdata(pdev); bcm_pr_debug("%s()\n", __func__); bcm_assert(1 == bs->ref_count); /* reset spi block */ bcm_spi_writeb(bs, 0, SPI_INT_MASK); /* HW shutdown */ clk_disable(bs->clk); devm_free_irq(&(pdev->dev), bs->irq, bs); devm_iounmap(&(pdev->dev), bs->regs); devm_release_mem_region(&(pdev->dev), bs->res_start, bs->res_size); platform_set_drvdata(pdev, NULL); clk_put(bs->clk); bs->ref_count = 0; return (0); }
int __init bcm_mpi_init(bcm_mpi_t *t, const bcm_mpi_params_t *params) { int ret = -1; #ifdef BCMPH_USE_SPI_DRIVER # ifndef BCMPH_NOHW struct spi_master *master; struct spi_board_info board_info; # endif // !BCMPH_NOHW #endif // BCMPH_USE_SPI_DRIVER bcm_pr_debug("%s()\n", __func__); bcm_assert(NULL != params); #ifndef BCMPH_NOHW t->trx_opts.fill_byte = params->fill_byte; t->trx_opts.wait_completion_with_irq = params->wait_completion_with_irq; t->trx_opts.drop_cs_after_each_byte = params->drop_cs_after_each_byte; t->trx_opts.cs_off_clk_cycles = params->cs_off_clk_cycles; #endif // !BCMPH_NOHW #ifdef BCMPH_USE_SPI_DRIVER t->mpi_clk = params->clk; # ifndef BCMPH_NOHW master = spi_busnum_to_master(params->bus_num); if (NULL == master) { bcm_pr_err("No SPI master found for bus num %d. Module bcm63xx-spi not loaded ?\n", (int)(params->bus_num)); ret = -EINVAL; goto fail_master; } memset(&(board_info), 0, sizeof(board_info)); strcpy(board_info.modalias, driver_name); board_info.max_speed_hz = params->clk; board_info.bus_num = params->bus_num; board_info.chip_select = params->cs; board_info.mode = SPI_MODE_3; t->dev = spi_new_device(master, &(board_info)); if (NULL == t->dev) { bcm_pr_err("Failed to add SPI device (busnum = %d, chip select = %d, clock = %lu)\n", (int)(params->bus_num), (int)(params->cs), (unsigned long)(params->clk)); ret = -ENOMEM; goto fail_new_dev; } put_device(&(master->dev)); /* Lock the bus */ if ((params->has_exclusive_bus_access) && (!bcm_drv_param_mpi_no_exclusive_bus_access)) { spi_bus_lock(t->dev->master); t->bus_is_locked = true; } bcm_mpi_enable_extra_CSs(params->cs); # ifdef BCMPH_DEBUG_MPI t->trace_len = 0; # endif // BCMPH_DEBUG_MPI return (0); spi_unregister_device(t->dev); fail_new_dev: put_device(&(master->dev)); fail_master: # else // BCMPH_NOHW ret = 0; # endif // BCMPH_NOHW #else // !BCMPH_USE_SPI_DRIVER # ifndef BCMPH_NOHW t->mpi_cs = params->cs; t->clk_cfg = bcm_mpi_get_clk_cfg(params->clk, params->cs_off_clk_cycles); bcm_mpi_enable_extra_CSs(params->cs); if (bcm_mpi_dev_data.ref_count <= 0) { struct device_driver *spi_driver = driver_find(bcm63xx_spi_driver.driver.name, &platform_bus_type); if (NULL != spi_driver) { bcm_pr_err("Error: Driver '%s' is already registered, aborting...\n", bcm63xx_spi_driver.driver.name); ret = -EBUSY; } else { ret = platform_driver_register(&(bcm63xx_spi_driver)); } bcm_assert(((ret) && (0 == bcm_mpi_dev_data.ref_count)) || ((!ret) && (1 == bcm_mpi_dev_data.ref_count))); } else { bcm_mpi_dev_data.ref_count += 1; ret = 0; } if (!ret) { bcm_assert(bcm_mpi_dev_data.ref_count > 0); if (params->cs > bcm_mpi_dev_data.num_chipselect) { dev_err(&(bcm_mpi_dev_data.pdev->dev), "%s, unsupported slave %d\n", __func__, params->cs); if (1 == bcm_mpi_dev_data.ref_count) { platform_driver_unregister(&(bcm63xx_spi_driver)); } bcm_mpi_dev_data.ref_count -= 1; ret = -EINVAL; } else { t->dev_data = &(bcm_mpi_dev_data); } } # else // BCMPH_NOHW ret = 0; # endif // BCMPH_NOHW #endif // !BCMPH_USE_SPI_DRIVER return (ret); }
int bcm_mpi_read(bcm_mpi_t *t, __u8 *buf, __u8 buf_len, const __u8 *prepend_buf, __u8 prepend_buf_len) { int ret = 0; #ifdef BCMPH_USE_SPI_DRIVER # ifndef BCMPH_NOHW struct spi_transfer tr0; struct spi_transfer tr1; struct spi_message msg; # endif // !BCMPH_NOHW dd_bcm_pr_debug("%s(buf_len=%d, prepend_buf_len=%d)\n", __func__, (int)(buf_len), (int)(prepend_buf_len)); # ifndef BCMPH_NOHW # ifdef BCMPH_DEBUG_MPI bcm_mpi_add_trace(t, MPI_WRITE, prepend_buf, prepend_buf_len); # endif // BCMPH_DEBUG_MPI spi_message_init(&(msg)); if (prepend_buf_len > 0) { memset(&(tr0), 0, sizeof(tr0)); tr0.tx_buf = prepend_buf; tr0.len = prepend_buf_len; tr0.bits_per_word = 8; tr0.speed_hz = t->mpi_clk; spi_message_add_tail(&(tr0), &(msg)); } memset(&(tr1), 0, sizeof(tr1)); tr1.rx_buf = buf; tr1.len = buf_len; tr1.bits_per_word = 8; tr1.speed_hz = t->mpi_clk; spi_message_add_tail(&(tr1), &(msg)); ret = bcm_make_transfer(t, &(msg)); # ifdef BCMPH_DEBUG_MPI bcm_mpi_add_trace(t, MPI_READ, buf, ret); # endif // BCMPH_DEBUG_MPI # endif // !BCMPH_NOHW #else // !BCMPH_USE_SPI_DRIVER dd_bcm_pr_debug("%s(buf_len=%d, prepend_buf_len=%d)\n", __func__, (int)(buf_len), (int)(prepend_buf_len)); # ifndef BCMPH_NOHW bcm_assert(buf_len <= t->dev_data->fifo_size); # ifdef BCMPH_DEBUG_MPI bcm_mpi_add_trace(t, MPI_WRITE, prepend_buf, prepend_buf_len); # endif // BCMPH_DEBUG_MPI bcm_mpi_setup_transfer(t); do { // Empty loop if (buf_len > 0) { if (prepend_buf_len > BCM63XX_SPI_MAX_PREPEND) { ret = bcm_mpi_rw_buf(t, (u8 *)(prepend_buf), prepend_buf_len, true, false, NULL, 0); if (ret != prepend_buf_len) { break; } prepend_buf = NULL; prepend_buf_len = 0; } ret = bcm_mpi_rw_buf(t, buf, buf_len, false, true, prepend_buf, prepend_buf_len); } else if (prepend_buf_len > 0) { ret = bcm_mpi_rw_buf(t, (u8 *)(prepend_buf), prepend_buf_len, true, false, NULL, 0); } else { ret = 0; } } while (0); # ifdef BCMPH_DEBUG_MPI bcm_mpi_add_trace(t, MPI_READ, buf, ret); # endif // BCMPH_DEBUG_MPI # endif // !BCMPH_NOHW #endif // !BCMPH_USE_SPI_DRIVER return (ret); }
static int bcm_mpi_rw_buf(bcm_mpi_t *t, u8 *buf, u8 buf_len, bool do_tx, bool do_rx, const u8 *prepend_buf, u8 prepend_buf_len) { bcm_mpi_dev_data_t *bs = t->dev_data; u16 msg_ctl; u16 cmd; unsigned int timeout; u8 rx_tail; dd_bcm_pr_debug("%s(buf_len=%u, do_tx=%d, do_rx=%d, prepend_buf_len=%u)\n", __func__, (unsigned int)(buf_len), (int)(do_tx), (int)(do_rx), (unsigned int)(prepend_buf_len)); /* Disable the CMD_DONE interrupt */ bcm_spi_writeb(bs, 0, SPI_INT_MASK); cmd = SPI_CMD_START_IMMEDIATE; cmd |= (t->mpi_cs << SPI_CMD_DEVICE_ID_SHIFT); if (t->trx_opts.drop_cs_after_each_byte) { cmd |= (1 << SPI_CMD_ONE_BYTE_SHIFT); } if (do_tx) { bcm_assert((0 == prepend_buf_len) && (buf_len > 0)); memcpy_toio(bs->tx_io, buf, buf_len); } else { bcm_assert(do_rx); if (prepend_buf_len > 0) { memcpy_toio(bs->tx_io, prepend_buf, prepend_buf_len); cmd |= (prepend_buf_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); } else { bcm_assert(buf_len > 0); } } if (t->trx_opts.wait_completion_with_irq) { init_completion(&(bs->done)); } /* Fill in the Message control register */ msg_ctl = (buf_len << SPI_BYTE_CNT_SHIFT); if (do_tx) { if (do_rx) { msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); } else { msg_ctl |= (SPI_HD_W << bs->msg_type_shift); } } else { msg_ctl |= (SPI_HD_R << bs->msg_type_shift); } switch (bs->msg_ctl_width) { case 8: bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL); break; case 16: bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL); break; default: bcm_assert(0); break; } bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); if (t->trx_opts.wait_completion_with_irq) { /* Enable the CMD_DONE interrupt */ bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); } /* Issue the transfer */ bcm_spi_writew(bs, cmd, SPI_CMD); if (t->trx_opts.wait_completion_with_irq) { timeout = wait_for_completion_timeout(&bs->done, HZ); if (!timeout) { return (-ETIMEDOUT); } } else { while (!(bcm_spi_readb(bs, SPI_INT_STATUS) & SPI_INTR_CMD_DONE)) { } } if (do_rx) { /* read out all data */ rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); if (rx_tail != buf_len) { return (-EINVAL); } if (rx_tail > 0) { memcpy_fromio(buf, bs->rx_io, buf_len); } } return (buf_len); }