[PATCH V10 2/2] mtd: spi-nor: Add driver for Cadence Quad SPI Flash Controller.
Marek Vasut
marex at denx.de
Thu May 12 17:24:46 PDT 2016
On 05/13/2016 02:00 AM, Trent Piepho wrote:
> On Mon, 2016-01-11 at 05:34 +0100, Marek Vasut wrote:
>> From: Graham Moore <grmoore-yzvPICuk2ABMcg4IHK0kFoH6Mc4MB0Vx at public.gmane.org>
>>
>> Add support for the Cadence QSPI controller. This controller is
>> present in the Altera SoCFPGA SoCs and this driver has been tested
>> on the Cyclone V SoC.
>
> I'm trying to use this driver the Alaric Devkit for Altera's Arria10
> SoC. It's not working so far. In the course of trying to debug it,
> I've found a few things with the driver in the socfpga-4.1-ltsi branch.
So are you trying to debug this driver or some other out-of-tree driver?
btw. I pushed the latest version here:
https://git.kernel.org/cgit/linux/kernel/git/marex/linux-2.6.git/log/?h=next/cadence-qspi
> However most of them are in code that's not present in this driver,
> which is the newest post of the code I could find to reply to.
I will check the rest later, thanks!
>> + CQSPI_REG_IRQ_WATERMARK | \
>> + CQSPI_REG_IRQ_UNDERFLOW)
>> +
>> +#define CQSPI_IRQ_STATUS_MASK 0x1FFFF
>> +
>
> Perhaps a comment.
> /* waits for all bits set in mask to be zero (clear==false) or one
> (clear==true) */
>
>> +static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clear)
>> +{
>> + unsigned long end = jiffies + msecs_to_jiffies(CQSPI_TIMEOUT_MS);
>> + u32 val;
>> +
>> + while (1) {
>> + val = readl(reg);
>> + if (clear)
>> + val = ~val;
>> + val &= mask;
>> +
>> + if (val == mask)
>> + return 0;
>
> Somewhat simpler.
>
> if ((readl(reg) & mask) == (clear ? 0 : mask))
> return 0;
>
>> +
>> + if (time_after(jiffies, end))
>> + return -ETIMEDOUT;
>
> Note that there is a hypervisor/vm/long hardirq etc. bug that can happen
> without timeouts like this. What happens is after the check of the bits
> fails, there is a very long delay before this task runs again. This can
> be easy if one is running under virtualization. The the time_after call
> reports the timeout expired. But the last time it the bit was checked,
> before the long delay, was well before the timeout expired. The way to
> avoid this is to always be sure to check the condition once after the
> timeout expired. This is sure to give the full timeout.
>
>
>> + }
>> +}
>> +
>
>> +
>> +static unsigned int calculate_ticks_for_ns(const unsigned int ref_clk_hz,
>> + const unsigned int ns_val)
>> +{
>> + unsigned int ticks;
>> +
>> + ticks = ref_clk_hz / 1000; /* kHz */
> This division doesn't round up.
>
>> + ticks = DIV_ROUND_UP(ticks * ns_val, 1000000);
> But this one does.
>
>> +
>> + return ticks;
>> +}
>> +
>> +static void cqspi_delay(struct spi_nor *nor, const unsigned int sclk_hz)
>> +{
>> + struct cqspi_flash_pdata *f_pdata = nor->priv;
>> + struct cqspi_st *cqspi = f_pdata->cqspi;
>
> Isn't the sclk_hz parameter of this function already available here as
> cqspi->sclk?
>
>> + void __iomem *iobase = cqspi->iobase;
>> + const unsigned int ref_clk_hz = cqspi->master_ref_clk_hz;
>> + unsigned int tshsl, tchsh, tslch, tsd2d;
>> + unsigned int reg;
>> + unsigned int tsclk;
>> +
>> + /* calculate the number of ref ticks for one sclk tick */
>> + tsclk = (ref_clk_hz + sclk_hz - 1) / sclk_hz;
>
> DIV_ROUND_UP(ref_clk_hz, sclk_hz);
>
>> +
>> + tshsl = calculate_ticks_for_ns(ref_clk_hz, f_pdata->tshsl_ns);
>> + /* this particular value must be at least one sclk */
>> + if (tshsl < tsclk)
>> + tshsl = tsclk;
>> +
>> + tchsh = calculate_ticks_for_ns(ref_clk_hz, f_pdata->tchsh_ns);
>> + tslch = calculate_ticks_for_ns(ref_clk_hz, f_pdata->tslch_ns);
>> + tsd2d = calculate_ticks_for_ns(ref_clk_hz, f_pdata->tsd2d_ns);
>> +
>> + reg = (tshsl & CQSPI_REG_DELAY_TSHSL_MASK)
>> + << CQSPI_REG_DELAY_TSHSL_LSB;
>> + reg |= (tchsh & CQSPI_REG_DELAY_TCHSH_MASK)
>> + << CQSPI_REG_DELAY_TCHSH_LSB;
>> + reg |= (tslch & CQSPI_REG_DELAY_TSLCH_MASK)
>> + << CQSPI_REG_DELAY_TSLCH_LSB;
>> + reg |= (tsd2d & CQSPI_REG_DELAY_TSD2D_MASK)
>> + << CQSPI_REG_DELAY_TSD2D_LSB;
>> + writel(reg, iobase + CQSPI_REG_DELAY);
>> +}
>> +
>> +static void cqspi_config_baudrate_div(struct cqspi_st *cqspi,
>> + const unsigned int sclk_hz)
>> +{
>
> sclk_hz is available as cqspi->sclk.
>
>> + const unsigned int ref_clk_hz = cqspi->master_ref_clk_hz;
>> + void __iomem *reg_base = cqspi->iobase;
>> + unsigned int reg;
>> + unsigned int div;
>> +
>> + reg = readl(reg_base + CQSPI_REG_CONFIG);
>> + reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB);
>> +
>> + div = ref_clk_hz / sclk_hz;
>
> Should this round up too?
>
>> +
>> + /* Recalculate the baudrate divisor based on QSPI specification. */
>> + if (div > 32)
>> + div = 32;
>> +
>> + /* Check if even number. */
>> + if (div & 1)
>> + div = (div / 2);
>> + else
>> + div = (div / 2) - 1;
>
> Wouldn't this be the same as div = DIV_ROUND_UP(div, 2) - 1;
>
> The entire div calculation could be done with:
>
> /* Register programmed with divider minus 1 */
> div = DIV_ROUND_UP(ref_clk_hz, s_clk_hz * 2) - 1;
> if (div > 15)
> div = 15;
>
>
>> +
>> + div = (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB;
>> + reg |= div;
>> + writel(reg, reg_base + CQSPI_REG_CONFIG);
>> +}
>> +
>
--
Best regards,
Marek Vasut
More information about the linux-mtd
mailing list