diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-29 16:28:36 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-29 16:28:36 +0200 |
commit | 18c0635363364ca2fc2d1cbd65bbf918daf89d1a (patch) | |
tree | 6fd1975977b4ab190b77952ee8e263f66e3dc5f7 /drivers/spi/spi-pl022.c | |
parent | Merge branch 'gpio/next' of git://git.secretlab.ca/git/linux-2.6 (diff) | |
parent | drivercore: Add helper macro for platform_driver boilerplate (diff) | |
download | linux-18c0635363364ca2fc2d1cbd65bbf918daf89d1a.tar.xz linux-18c0635363364ca2fc2d1cbd65bbf918daf89d1a.zip |
Merge branch 'spi/next' of git://git.secretlab.ca/git/linux-2.6
* 'spi/next' of git://git.secretlab.ca/git/linux-2.6:
drivercore: Add helper macro for platform_driver boilerplate
spi: irq: Remove IRQF_DISABLED
OMAP: SPI: Fix the trying to free nonexistent resource error
spi/spi-ep93xx: add module.h include
spi/tegra: fix compilation error in spi-tegra.c
spi: spi-dw: fix all sparse warnings
spi/spi-pl022: Call pl022_dma_remove(pl022) only if enable_dma is true
spi/spi-pl022: calculate_effective_freq() must set rate <= requested rate
spi/spi-pl022: Don't allocate more sg than required.
spi/spi-pl022: Use GFP_ATOMIC for allocation from tasklet
spi/spi-pl022: Resolve formatting issues
Diffstat (limited to 'drivers/spi/spi-pl022.c')
-rw-r--r-- | drivers/spi/spi-pl022.c | 146 |
1 files changed, 68 insertions, 78 deletions
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 1ab2fa0d37fd..f103e470cb63 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -113,7 +113,6 @@ #define SSP_CR0_MASK_CSS_ST (0x1FUL << 16) #define SSP_CR0_MASK_FRF_ST (0x3UL << 21) - /* * SSP Control Register 0 - SSP_CR1 */ @@ -283,7 +282,6 @@ #define SPI_POLLING_TIMEOUT 1000 - /* * The type of reading going on on this chip */ @@ -749,7 +747,6 @@ static void readwriter(struct pl022 *pl022) */ } - /** * next_transfer - Move to the Next transfer in the current spi message * @pl022: SSP driver private data structure @@ -1016,14 +1013,14 @@ static int configure_dma(struct pl022 *pl022) dmaengine_slave_config(txchan, &tx_conf); /* Create sglists for the transfers */ - pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1; + pages = DIV_ROUND_UP(pl022->cur_transfer->len, PAGE_SIZE); dev_dbg(&pl022->adev->dev, "using %d pages for transfer\n", pages); - ret = sg_alloc_table(&pl022->sgt_rx, pages, GFP_KERNEL); + ret = sg_alloc_table(&pl022->sgt_rx, pages, GFP_ATOMIC); if (ret) goto err_alloc_rx_sg; - ret = sg_alloc_table(&pl022->sgt_tx, pages, GFP_KERNEL); + ret = sg_alloc_table(&pl022->sgt_tx, pages, GFP_ATOMIC); if (ret) goto err_alloc_tx_sg; @@ -1531,8 +1528,7 @@ static void pump_messages(struct work_struct *work) /* Initial message state */ pl022->cur_msg->state = STATE_START; pl022->cur_transfer = list_entry(pl022->cur_msg->transfers.next, - struct spi_transfer, - transfer_list); + struct spi_transfer, transfer_list); /* Setup the SPI using the per chip configuration */ pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi); @@ -1551,7 +1547,6 @@ static void pump_messages(struct work_struct *work) do_interrupt_dma_transfer(pl022); } - static int __init init_queue(struct pl022 *pl022) { INIT_LIST_HEAD(&pl022->queue); @@ -1560,8 +1555,8 @@ static int __init init_queue(struct pl022 *pl022) pl022->running = false; pl022->busy = false; - tasklet_init(&pl022->pump_transfers, - pump_transfers, (unsigned long)pl022); + tasklet_init(&pl022->pump_transfers, pump_transfers, + (unsigned long)pl022); INIT_WORK(&pl022->pump_messages, pump_messages); pl022->workqueue = create_singlethread_workqueue( @@ -1572,7 +1567,6 @@ static int __init init_queue(struct pl022 *pl022) return 0; } - static int start_queue(struct pl022 *pl022) { unsigned long flags; @@ -1595,7 +1589,6 @@ static int start_queue(struct pl022 *pl022) return 0; } - static int stop_queue(struct pl022 *pl022) { unsigned long flags; @@ -1791,71 +1784,70 @@ static int pl022_transfer(struct spi_device *spi, struct spi_message *msg) return 0; } -static int calculate_effective_freq(struct pl022 *pl022, - int freq, - struct ssp_clock_params *clk_freq) +static inline u32 spi_rate(u32 rate, u16 cpsdvsr, u16 scr) +{ + return rate / (cpsdvsr * (1 + scr)); +} + +static int calculate_effective_freq(struct pl022 *pl022, int freq, struct + ssp_clock_params * clk_freq) { /* Lets calculate the frequency parameters */ - u16 cpsdvsr = 2; - u16 scr = 0; - bool freq_found = false; - u32 rate; - u32 max_tclk; - u32 min_tclk; + u16 cpsdvsr = CPSDVR_MIN, scr = SCR_MIN; + u32 rate, max_tclk, min_tclk, best_freq = 0, best_cpsdvsr = 0, + best_scr = 0, tmp, found = 0; rate = clk_get_rate(pl022->clk); /* cpsdvscr = 2 & scr 0 */ - max_tclk = (rate / (CPSDVR_MIN * (1 + SCR_MIN))); + max_tclk = spi_rate(rate, CPSDVR_MIN, SCR_MIN); /* cpsdvsr = 254 & scr = 255 */ - min_tclk = (rate / (CPSDVR_MAX * (1 + SCR_MAX))); - - if ((freq <= max_tclk) && (freq >= min_tclk)) { - while (cpsdvsr <= CPSDVR_MAX && !freq_found) { - while (scr <= SCR_MAX && !freq_found) { - if ((rate / - (cpsdvsr * (1 + scr))) > freq) - scr += 1; - else { - /* - * This bool is made true when - * effective frequency >= - * target frequency is found - */ - freq_found = true; - if ((rate / - (cpsdvsr * (1 + scr))) != freq) { - if (scr == SCR_MIN) { - cpsdvsr -= 2; - scr = SCR_MAX; - } else - scr -= 1; - } - } - } - if (!freq_found) { - cpsdvsr += 2; - scr = SCR_MIN; - } - } - if (cpsdvsr != 0) { - dev_dbg(&pl022->adev->dev, - "SSP Effective Frequency is %u\n", - (rate / (cpsdvsr * (1 + scr)))); - clk_freq->cpsdvsr = (u8) (cpsdvsr & 0xFF); - clk_freq->scr = (u8) (scr & 0xFF); - dev_dbg(&pl022->adev->dev, - "SSP cpsdvsr = %d, scr = %d\n", - clk_freq->cpsdvsr, clk_freq->scr); - } - } else { + min_tclk = spi_rate(rate, CPSDVR_MAX, SCR_MAX); + + if (!((freq <= max_tclk) && (freq >= min_tclk))) { dev_err(&pl022->adev->dev, "controller data is incorrect: out of range frequency"); return -EINVAL; } + + /* + * best_freq will give closest possible available rate (<= requested + * freq) for all values of scr & cpsdvsr. + */ + while ((cpsdvsr <= CPSDVR_MAX) && !found) { + while (scr <= SCR_MAX) { + tmp = spi_rate(rate, cpsdvsr, scr); + + if (tmp > freq) + scr++; + /* + * If found exact value, update and break. + * If found more closer value, update and continue. + */ + else if ((tmp == freq) || (tmp > best_freq)) { + best_freq = tmp; + best_cpsdvsr = cpsdvsr; + best_scr = scr; + + if (tmp == freq) + break; + } + scr++; + } + cpsdvsr += 2; + scr = SCR_MIN; + } + + clk_freq->cpsdvsr = (u8) (best_cpsdvsr & 0xFF); + clk_freq->scr = (u8) (best_scr & 0xFF); + dev_dbg(&pl022->adev->dev, + "SSP Target Frequency is: %u, Effective Frequency is %u\n", + freq, best_freq); + dev_dbg(&pl022->adev->dev, "SSP cpsdvsr = %d, scr = %d\n", + clk_freq->cpsdvsr, clk_freq->scr); + return 0; } - /* * A piece of default chip info unless the platform * supplies it. @@ -1873,7 +1865,6 @@ static const struct pl022_config_chip pl022_default_chip_info = { .cs_control = null_cs_control, }; - /** * pl022_setup - setup function registered to SPI master framework * @spi: spi device which is requesting setup @@ -1950,7 +1941,6 @@ static int pl022_setup(struct spi_device *spi) goto err_config_params; } - status = verify_controller_parameters(pl022, chip_info); if (status) { dev_err(&spi->dev, "controller data is incorrect"); @@ -2090,7 +2080,8 @@ static int pl022_setup(struct spi_device *spi) } SSP_WRITE_BITS(chip->cr1, SSP_DISABLED, SSP_CR1_MASK_SSE, 1); SSP_WRITE_BITS(chip->cr1, chip_info->hierarchy, SSP_CR1_MASK_MS, 2); - SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, 3); + SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, + 3); /* Save controller_state */ spi_set_ctldata(spi, chip); @@ -2116,7 +2107,6 @@ static void pl022_cleanup(struct spi_device *spi) kfree(chip); } - static int __devinit pl022_probe(struct amba_device *adev, const struct amba_id *id) { @@ -2242,7 +2232,9 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) err_start_queue: err_init_queue: destroy_queue(pl022); - pl022_dma_remove(pl022); + if (platform_info->enable_dma) + pl022_dma_remove(pl022); + free_irq(adev->irq[0], pl022); err_no_irq: clk_unprepare(pl022->clk); @@ -2277,7 +2269,9 @@ pl022_remove(struct amba_device *adev) if (destroy_queue(pl022) != 0) dev_err(&adev->dev, "queue remove failed\n"); load_ssp_default_config(pl022); - pl022_dma_remove(pl022); + if (pl022->master_info->enable_dma) + pl022_dma_remove(pl022); + free_irq(adev->irq[0], pl022); clk_disable(pl022->clk); clk_unprepare(pl022->clk); @@ -2364,7 +2358,6 @@ static struct vendor_data vendor_arm = { .loopback = true, }; - static struct vendor_data vendor_st = { .fifodepth = 32, .max_bpw = 32, @@ -2419,9 +2412,9 @@ static struct amba_id pl022_ids[] = { * and 32 locations deep TX/RX FIFO but no extended * CR0/CR1 register */ - .id = 0x00080023, - .mask = 0xffffffff, - .data = &vendor_st_pl023, + .id = 0x00080023, + .mask = 0xffffffff, + .data = &vendor_st_pl023, }, { .id = 0x10080023, @@ -2441,19 +2434,16 @@ static struct amba_driver pl022_driver = { .remove = __devexit_p(pl022_remove), }; - static int __init pl022_init(void) { return amba_driver_register(&pl022_driver); } - subsys_initcall(pl022_init); static void __exit pl022_exit(void) { amba_driver_unregister(&pl022_driver); } - module_exit(pl022_exit); MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>"); |