diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/host/mmci.c | 41 | ||||
-rw-r--r-- | drivers/rtc/rtc-pl031.c | 365 | ||||
-rw-r--r-- | drivers/spi/amba-pl022.c | 18 |
3 files changed, 366 insertions, 58 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 90d168ad03b..84c103a7ee1 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -2,6 +2,7 @@ * linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver * * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * Copyright (C) 2010 ST-Ericsson AB. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -34,9 +35,6 @@ #define DRIVER_NAME "mmci-pl18x" -#define DBG(host,fmt,args...) \ - pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args) - static unsigned int fmax = 515633; /* @@ -105,8 +103,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) void __iomem *base; int blksz_bits; - DBG(host, "blksz %04x blks %04x flags %08x\n", - data->blksz, data->blocks, data->flags); + dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n", + data->blksz, data->blocks, data->flags); host->data = data; host->size = data->blksz; @@ -155,7 +153,7 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) { void __iomem *base = host->base; - DBG(host, "op %02x arg %08x flags %08x\n", + dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n", cmd->opcode, cmd->arg, cmd->flags); if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) { @@ -184,8 +182,20 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, { if (status & MCI_DATABLOCKEND) { host->data_xfered += data->blksz; +#ifdef CONFIG_ARCH_U300 + /* + * On the U300 some signal or other is + * badly routed so that a data write does + * not properly terminate with a MCI_DATAEND + * status flag. This quirk will make writes + * work again. + */ + if (data->flags & MMC_DATA_WRITE) + status |= MCI_DATAEND; +#endif } if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { + dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ (status %08x)\n", status); if (status & MCI_DATACRCFAIL) data->error = -EILSEQ; else if (status & MCI_DATATIMEOUT) @@ -307,7 +317,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) status = readl(base + MMCISTATUS); - DBG(host, "irq1 %08x\n", status); + dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); do { unsigned long flags; @@ -401,7 +411,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) status &= readl(host->base + MMCIMASK0); writel(status, host->base + MMCICLEAR); - DBG(host, "irq0 %08x\n", status); + dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status); data = host->data; if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN| @@ -428,8 +438,8 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(host->mrq != NULL); if (mrq->data && !is_power_of_2(mrq->data->blksz)) { - printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n", - mmc_hostname(mmc), mrq->data->blksz); + dev_err(mmc_dev(mmc), "unsupported block size (%d bytes)\n", + mrq->data->blksz); mrq->cmd->error = -EINVAL; mmc_request_done(mmc, mrq); return; @@ -582,8 +592,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) host->hw_designer = amba_manf(dev); host->hw_revision = amba_rev(dev); - DBG(host, "designer ID = 0x%02x\n", host->hw_designer); - DBG(host, "revision = 0x%01x\n", host->hw_revision); + dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer); + dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision); host->clk = clk_get(&dev->dev, NULL); if (IS_ERR(host->clk)) { @@ -608,7 +618,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) if (ret < 0) goto clk_disable; host->mclk = clk_get_rate(host->clk); - DBG(host, "eventual mclk rate: %u Hz\n", host->mclk); + dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n", + host->mclk); } host->base = ioremap(dev->res.start, resource_size(&dev->res)); if (!host->base) { @@ -619,6 +630,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) mmc->ops = &mmci_ops; mmc->f_min = (host->mclk + 511) / 512; mmc->f_max = min(host->mclk, fmax); + dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); + #ifdef CONFIG_REGULATOR /* If we're using the regulator framework, try to fetch a regulator */ host->vcc = regulator_get(&dev->dev, "vmmc"); @@ -712,7 +725,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) mmc_add_host(mmc); - printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n", + dev_info(&dev->dev, "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n", mmc_hostname(mmc), amba_rev(dev), amba_config(dev), (unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]); diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 0264b117893..c256aacfa95 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -7,6 +7,9 @@ * * Copyright 2006 (c) MontaVista Software, Inc. * + * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> + * Copyright 2010 (c) ST-Ericsson AB + * * 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 @@ -18,6 +21,9 @@ #include <linux/interrupt.h> #include <linux/amba/bus.h> #include <linux/io.h> +#include <linux/bcd.h> +#include <linux/delay.h> +#include <linux/version.h> /* * Register definitions @@ -30,35 +36,207 @@ #define RTC_RIS 0x14 /* Raw interrupt status register */ #define RTC_MIS 0x18 /* Masked interrupt status register */ #define RTC_ICR 0x1c /* Interrupt clear register */ +/* ST variants have additional timer functionality */ +#define RTC_TDR 0x20 /* Timer data read register */ +#define RTC_TLR 0x24 /* Timer data load register */ +#define RTC_TCR 0x28 /* Timer control register */ +#define RTC_YDR 0x30 /* Year data read register */ +#define RTC_YMR 0x34 /* Year match register */ +#define RTC_YLR 0x38 /* Year data load register */ + +#define RTC_CR_CWEN (1 << 26) /* Clockwatch enable bit */ + +#define RTC_TCR_EN (1 << 1) /* Periodic timer enable bit */ + +/* Common bit definitions for Interrupt status and control registers */ +#define RTC_BIT_AI (1 << 0) /* Alarm interrupt bit */ +#define RTC_BIT_PI (1 << 1) /* Periodic interrupt bit. ST variants only. */ + +/* Common bit definations for ST v2 for reading/writing time */ +#define RTC_SEC_SHIFT 0 +#define RTC_SEC_MASK (0x3F << RTC_SEC_SHIFT) /* Second [0-59] */ +#define RTC_MIN_SHIFT 6 +#define RTC_MIN_MASK (0x3F << RTC_MIN_SHIFT) /* Minute [0-59] */ +#define RTC_HOUR_SHIFT 12 +#define RTC_HOUR_MASK (0x1F << RTC_HOUR_SHIFT) /* Hour [0-23] */ +#define RTC_WDAY_SHIFT 17 +#define RTC_WDAY_MASK (0x7 << RTC_WDAY_SHIFT) /* Day of Week [1-7] 1=Sunday */ +#define RTC_MDAY_SHIFT 20 +#define RTC_MDAY_MASK (0x1F << RTC_MDAY_SHIFT) /* Day of Month [1-31] */ +#define RTC_MON_SHIFT 25 +#define RTC_MON_MASK (0xF << RTC_MON_SHIFT) /* Month [1-12] 1=January */ + +#define RTC_TIMER_FREQ 32768 struct pl031_local { struct rtc_device *rtc; void __iomem *base; + u8 hw_designer; + u8 hw_revision:4; }; -static irqreturn_t pl031_interrupt(int irq, void *dev_id) +static int pl031_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + unsigned long imsc; + + /* Clear any pending alarm interrupts. */ + writel(RTC_BIT_AI, ldata->base + RTC_ICR); + + imsc = readl(ldata->base + RTC_IMSC); + + if (enabled == 1) + writel(imsc | RTC_BIT_AI, ldata->base + RTC_IMSC); + else + writel(imsc & ~RTC_BIT_AI, ldata->base + RTC_IMSC); + + return 0; +} + +/* + * Convert Gregorian date to ST v2 RTC format. + */ +static int pl031_stv2_tm_to_time(struct device *dev, + struct rtc_time *tm, unsigned long *st_time, + unsigned long *bcd_year) +{ + int year = tm->tm_year + 1900; + int wday = tm->tm_wday; + + /* wday masking is not working in hardware so wday must be valid */ + if (wday < -1 || wday > 6) { + dev_err(dev, "invalid wday value %d\n", tm->tm_wday); + return -EINVAL; + } else if (wday == -1) { + /* wday is not provided, calculate it here */ + unsigned long time; + struct rtc_time calc_tm; + + rtc_tm_to_time(tm, &time); + rtc_time_to_tm(time, &calc_tm); + wday = calc_tm.tm_wday; + } + + *bcd_year = (bin2bcd(year % 100) | bin2bcd(year / 100) << 8); + + *st_time = ((tm->tm_mon + 1) << RTC_MON_SHIFT) + | (tm->tm_mday << RTC_MDAY_SHIFT) + | ((wday + 1) << RTC_WDAY_SHIFT) + | (tm->tm_hour << RTC_HOUR_SHIFT) + | (tm->tm_min << RTC_MIN_SHIFT) + | (tm->tm_sec << RTC_SEC_SHIFT); + + return 0; +} + +/* + * Convert ST v2 RTC format to Gregorian date. + */ +static int pl031_stv2_time_to_tm(unsigned long st_time, unsigned long bcd_year, + struct rtc_time *tm) +{ + tm->tm_year = bcd2bin(bcd_year) + (bcd2bin(bcd_year >> 8) * 100); + tm->tm_mon = ((st_time & RTC_MON_MASK) >> RTC_MON_SHIFT) - 1; + tm->tm_mday = ((st_time & RTC_MDAY_MASK) >> RTC_MDAY_SHIFT); + tm->tm_wday = ((st_time & RTC_WDAY_MASK) >> RTC_WDAY_SHIFT) - 1; + tm->tm_hour = ((st_time & RTC_HOUR_MASK) >> RTC_HOUR_SHIFT); + tm->tm_min = ((st_time & RTC_MIN_MASK) >> RTC_MIN_SHIFT); + tm->tm_sec = ((st_time & RTC_SEC_MASK) >> RTC_SEC_SHIFT); + + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + tm->tm_year -= 1900; + + return 0; +} + +static int pl031_stv2_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + + pl031_stv2_time_to_tm(readl(ldata->base + RTC_DR), + readl(ldata->base + RTC_YDR), tm); + + return 0; +} + +static int pl031_stv2_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long time; + unsigned long bcd_year; + struct pl031_local *ldata = dev_get_drvdata(dev); + int ret; + + ret = pl031_stv2_tm_to_time(dev, tm, &time, &bcd_year); + if (ret == 0) { + writel(bcd_year, ldata->base + RTC_YLR); + writel(time, ldata->base + RTC_LR); + } + + return ret; +} + +static int pl031_stv2_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) { - struct rtc_device *rtc = dev_id; + struct pl031_local *ldata = dev_get_drvdata(dev); + int ret; - rtc_update_irq(rtc, 1, RTC_AF); + ret = pl031_stv2_time_to_tm(readl(ldata->base + RTC_MR), + readl(ldata->base + RTC_YMR), &alarm->time); - return IRQ_HANDLED; + alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI; + alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI; + + return ret; } -static int pl031_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +static int pl031_stv2_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct pl031_local *ldata = dev_get_drvdata(dev); + unsigned long time; + unsigned long bcd_year; + int ret; + + /* At the moment, we can only deal with non-wildcarded alarm times. */ + ret = rtc_valid_tm(&alarm->time); + if (ret == 0) { + ret = pl031_stv2_tm_to_time(dev, &alarm->time, + &time, &bcd_year); + if (ret == 0) { + writel(bcd_year, ldata->base + RTC_YMR); + writel(time, ldata->base + RTC_MR); + + pl031_alarm_irq_enable(dev, alarm->enabled); + } + } + + return ret; +} + +static irqreturn_t pl031_interrupt(int irq, void *dev_id) +{ + struct pl031_local *ldata = dev_id; + unsigned long rtcmis; + unsigned long events = 0; + + rtcmis = readl(ldata->base + RTC_MIS); + if (rtcmis) { + writel(rtcmis, ldata->base + RTC_ICR); + + if (rtcmis & RTC_BIT_AI) + events |= (RTC_AF | RTC_IRQF); + + /* Timer interrupt is only available in ST variants */ + if ((rtcmis & RTC_BIT_PI) && + (ldata->hw_designer == AMBA_VENDOR_ST)) + events |= (RTC_PF | RTC_IRQF); + + rtc_update_irq(ldata->rtc, 1, events); - switch (cmd) { - case RTC_AIE_OFF: - writel(1, ldata->base + RTC_MIS); - return 0; - case RTC_AIE_ON: - writel(0, ldata->base + RTC_MIS); - return 0; + return IRQ_HANDLED; } - return -ENOIOCTLCMD; + return IRQ_NONE; } static int pl031_read_time(struct device *dev, struct rtc_time *tm) @@ -74,11 +252,14 @@ static int pl031_set_time(struct device *dev, struct rtc_time *tm) { unsigned long time; struct pl031_local *ldata = dev_get_drvdata(dev); + int ret; - rtc_tm_to_time(tm, &time); - writel(time, ldata->base + RTC_LR); + ret = rtc_tm_to_time(tm, &time); - return 0; + if (ret == 0) + writel(time, ldata->base + RTC_LR); + + return ret; } static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) @@ -86,8 +267,9 @@ static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) struct pl031_local *ldata = dev_get_drvdata(dev); rtc_time_to_tm(readl(ldata->base + RTC_MR), &alarm->time); - alarm->pending = readl(ldata->base + RTC_RIS); - alarm->enabled = readl(ldata->base + RTC_IMSC); + + alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI; + alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI; return 0; } @@ -96,22 +278,71 @@ static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct pl031_local *ldata = dev_get_drvdata(dev); unsigned long time; + int ret; + + /* At the moment, we can only deal with non-wildcarded alarm times. */ + ret = rtc_valid_tm(&alarm->time); + if (ret == 0) { + ret = rtc_tm_to_time(&alarm->time, &time); + if (ret == 0) { + writel(time, ldata->base + RTC_MR); + pl031_alarm_irq_enable(dev, alarm->enabled); + } + } + + return ret; +} + +/* Periodic interrupt is only available in ST variants. */ +static int pl031_irq_set_state(struct device *dev, int enabled) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + + if (enabled == 1) { + /* Clear any pending timer interrupt. */ + writel(RTC_BIT_PI, ldata->base + RTC_ICR); + + writel(readl(ldata->base + RTC_IMSC) | RTC_BIT_PI, + ldata->base + RTC_IMSC); - rtc_tm_to_time(&alarm->time, &time); + /* Now start the timer */ + writel(readl(ldata->base + RTC_TCR) | RTC_TCR_EN, + ldata->base + RTC_TCR); - writel(time, ldata->base + RTC_MR); - writel(!alarm->enabled, ldata->base + RTC_MIS); + } else { + writel(readl(ldata->base + RTC_IMSC) & (~RTC_BIT_PI), + ldata->base + RTC_IMSC); + + /* Also stop the timer */ + writel(readl(ldata->base + RTC_TCR) & (~RTC_TCR_EN), + ldata->base + RTC_TCR); + } + /* Wait at least 1 RTC32 clock cycle to ensure next access + * to RTC_TCR will succeed. + */ + udelay(40); return 0; } -static const struct rtc_class_ops pl031_ops = { - .ioctl = pl031_ioctl, - .read_time = pl031_read_time, - .set_time = pl031_set_time, - .read_alarm = pl031_read_alarm, - .set_alarm = pl031_set_alarm, -}; +static int pl031_irq_set_freq(struct device *dev, int freq) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + + /* Cant set timer if it is already enabled */ + if (readl(ldata->base + RTC_TCR) & RTC_TCR_EN) { + dev_err(dev, "can't change frequency while timer enabled\n"); + return -EINVAL; + } + + /* If self start bit in RTC_TCR is set timer will start here, + * but we never set that bit. Instead we start the timer when + * set_state is called with enabled == 1. + */ + writel(RTC_TIMER_FREQ / freq, ldata->base + RTC_TLR); + + return 0; +} static int pl031_remove(struct amba_device *adev) { @@ -131,18 +362,20 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id) { int ret; struct pl031_local *ldata; + struct rtc_class_ops *ops = id->data; ret = amba_request_regions(adev, NULL); if (ret) goto err_req; - ldata = kmalloc(sizeof(struct pl031_local), GFP_KERNEL); + ldata = kzalloc(sizeof(struct pl031_local), GFP_KERNEL); if (!ldata) { ret = -ENOMEM; goto out; } ldata->base = ioremap(adev->res.start, resource_size(&adev->res)); + if (!ldata->base) { ret = -ENOMEM; goto out_no_remap; @@ -150,24 +383,36 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id) amba_set_drvdata(adev, ldata); - if (request_irq(adev->irq[0], pl031_interrupt, IRQF_DISABLED, - "rtc-pl031", ldata->rtc)) { - ret = -EIO; - goto out_no_irq; - } + ldata->hw_designer = amba_manf(adev); + ldata->hw_revision = amba_rev(adev); + + dev_dbg(&adev->dev, "designer ID = 0x%02x\n", ldata->hw_designer); + dev_dbg(&adev->dev, "revision = 0x%01x\n", ldata->hw_revision); - ldata->rtc = rtc_device_register("pl031", &adev->dev, &pl031_ops, - THIS_MODULE); + /* Enable the clockwatch on ST Variants */ + if ((ldata->hw_designer == AMBA_VENDOR_ST) && + (ldata->hw_revision > 1)) + writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN, + ldata->base + RTC_CR); + + ldata->rtc = rtc_device_register("pl031", &adev->dev, ops, + THIS_MODULE); if (IS_ERR(ldata->rtc)) { ret = PTR_ERR(ldata->rtc); goto out_no_rtc; } + if (request_irq(adev->irq[0], pl031_interrupt, + IRQF_DISABLED | IRQF_SHARED, "rtc-pl031", ldata)) { + ret = -EIO; + goto out_no_irq; + } + return 0; -out_no_rtc: - free_irq(adev->irq[0], ldata->rtc); out_no_irq: + rtc_device_unregister(ldata->rtc); +out_no_rtc: iounmap(ldata->base); amba_set_drvdata(adev, NULL); out_no_remap: @@ -175,13 +420,57 @@ out_no_remap: out: amba_release_regions(adev); err_req: + return ret; } +/* Operations for the original ARM version */ +static struct rtc_class_ops arm_pl031_ops = { + .read_time = pl031_read_time, + .set_time = pl031_set_time, + .read_alarm = pl031_read_alarm, + .set_alarm = pl031_set_alarm, + .alarm_irq_enable = pl031_alarm_irq_enable, +}; + +/* The First ST derivative */ +static struct rtc_class_ops stv1_pl031_ops = { + .read_time = pl031_read_time, + .set_time = pl031_set_time, + .read_alarm = pl031_read_alarm, + .set_alarm = pl031_set_alarm, + .alarm_irq_enable = pl031_alarm_irq_enable, + .irq_set_state = pl031_irq_set_state, + .irq_set_freq = pl031_irq_set_freq, +}; + +/* And the second ST derivative */ +static struct rtc_class_ops stv2_pl031_ops = { + .read_time = pl031_stv2_read_time, + .set_time = pl031_stv2_set_time, + .read_alarm = pl031_stv2_read_alarm, + .set_alarm = pl031_stv2_set_alarm, + .alarm_irq_enable = pl031_alarm_irq_enable, + .irq_set_state = pl031_irq_set_state, + .irq_set_freq = pl031_irq_set_freq, +}; + static struct amba_id pl031_ids[] __initdata = { { .id = 0x00041031, .mask = 0x000fffff, + .data = &arm_pl031_ops, + }, + /* ST Micro variants */ + { + .id = 0x00180031, + .mask = 0x00ffffff, + .data = &stv1_pl031_ops, + }, + { + .id = 0x00280031, + .mask = 0x00ffffff, + .data = &stv2_pl031_ops, }, {0, 0}, }; diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index ff5bbb9c43c..9aeb6811310 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -363,6 +363,7 @@ struct pl022 { void *rx_end; enum ssp_reading read; enum ssp_writing write; + u32 exp_fifo_level; }; /** @@ -501,6 +502,9 @@ static int flush(struct pl022 *pl022) while (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE) readw(SSP_DR(pl022->virtbase)); } while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_BSY) && limit--); + + pl022->exp_fifo_level = 0; + return limit; } @@ -583,10 +587,9 @@ static void readwriter(struct pl022 *pl022) * errons in 8bit wide transfers on ARM variants (just 8 words * FIFO, means only 8x8 = 64 bits in FIFO) at least. * - * FIXME: currently we have no logic to account for this. - * perhaps there is even something broken in HW regarding - * 8bit transfers (it doesn't fail on 16bit) so this needs - * more investigation... + * To prevent this issue, the TX FIFO is only filled to the + * unused RX FIFO fill length, regardless of what the TX + * FIFO status flag indicates. */ dev_dbg(&pl022->adev->dev, "%s, rx: %p, rxend: %p, tx: %p, txend: %p\n", @@ -613,11 +616,12 @@ static void readwriter(struct pl022 *pl022) break; } pl022->rx += (pl022->cur_chip->n_bytes); + pl022->exp_fifo_level--; } /* - * Write as much as you can, while keeping an eye on the RX FIFO! + * Write as much as possible up to the RX FIFO size */ - while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_TNF) + while ((pl022->exp_fifo_level < pl022->vendor->fifodepth) && (pl022->tx < pl022->tx_end)) { switch (pl022->write) { case WRITING_NULL: @@ -634,6 +638,7 @@ static void readwriter(struct pl022 *pl022) break; } pl022->tx += (pl022->cur_chip->n_bytes); + pl022->exp_fifo_level++; /* * This inner reader takes care of things appearing in the RX * FIFO as we're transmitting. This will happen a lot since the @@ -660,6 +665,7 @@ static void readwriter(struct pl022 *pl022) break; } pl022->rx += (pl022->cur_chip->n_bytes); + pl022->exp_fifo_level--; } } /* |