#include <dm.h>
#include <malloc.h>
#include <spi.h>
+#include <clk.h>
#include <wait_bit.h>
#include <asm/io.h>
#define MVEBU_SPI_A3700_CLK_POL BIT(7)
#define MVEBU_SPI_A3700_FIFO_EN BIT(17)
#define MVEBU_SPI_A3700_SPI_EN_0 BIT(16)
-#define MVEBU_SPI_A3700_CLK_PRESCALE_BIT 0
-#define MVEBU_SPI_A3700_CLK_PRESCALE_MASK \
- (0x1f << MVEBU_SPI_A3700_CLK_PRESCALE_BIT)
+#define MVEBU_SPI_A3700_CLK_PRESCALE_MASK 0x1f
+
/* SPI registers */
struct spi_reg {
struct mvebu_spi_platdata {
struct spi_reg *spireg;
- unsigned int frequency;
- unsigned int clock;
+ struct clk clk;
};
static void spi_cs_activate(struct spi_reg *reg, int cs)
{
struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
struct spi_reg *reg = plat->spireg;
- u32 data;
+ u32 data, prescale;
data = readl(®->cfg);
- /* Set Prescaler */
- data &= ~MVEBU_SPI_A3700_CLK_PRESCALE_MASK;
+ prescale = DIV_ROUND_UP(clk_get_rate(&plat->clk), hz);
+ if (prescale > 0x1f)
+ prescale = 0x1f;
+ else if (prescale > 0xf)
+ prescale = 0x10 + (prescale + 1) / 2;
- /* Calculate Prescaler = (spi_input_freq / spi_max_freq) */
- if (hz > plat->frequency)
- hz = plat->frequency;
- data |= plat->clock / hz;
+ data &= ~MVEBU_SPI_A3700_CLK_PRESCALE_MASK;
+ data |= prescale & MVEBU_SPI_A3700_CLK_PRESCALE_MASK;
writel(data, ®->cfg);
static int mvebu_spi_ofdata_to_platdata(struct udevice *bus)
{
struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
+ int ret;
plat->spireg = (struct spi_reg *)devfdt_get_addr(bus);
- /*
- * FIXME
- * Right now, mvebu does not have a clock infrastructure in U-Boot
- * which should be used to query the input clock to the SPI
- * controller. Once this clock driver is integrated into U-Boot
- * it should be used to read the input clock and the DT property
- * can be removed.
- */
- plat->clock = fdtdec_get_int(gd->fdt_blob, dev_of_offset(bus),
- "clock-frequency", 160000);
- plat->frequency = fdtdec_get_int(gd->fdt_blob, dev_of_offset(bus),
- "spi-max-frequency", 40000);
+ ret = clk_get_by_index(bus, 0, &plat->clk);
+ if (ret) {
+ dev_err(bus, "cannot get clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mvebu_spi_remove(struct udevice *bus)
+{
+ struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
+
+ clk_free(&plat->clk);
return 0;
}
.ofdata_to_platdata = mvebu_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct mvebu_spi_platdata),
.probe = mvebu_spi_probe,
+ .remove = mvebu_spi_remove,
};