in spi-orion.c [645:805]
static int orion_spi_probe(struct platform_device *pdev)
{
const struct orion_spi_dev *devdata;
struct spi_master *master;
struct orion_spi *spi;
struct resource *r;
unsigned long tclk_hz;
int status = 0;
struct device_node *np;
master = spi_alloc_master(&pdev->dev, sizeof(*spi));
if (master == NULL) {
dev_dbg(&pdev->dev, "master allocation failed\n");
return -ENOMEM;
}
if (pdev->id != -1)
master->bus_num = pdev->id;
if (pdev->dev.of_node) {
u32 cell_index;
if (!of_property_read_u32(pdev->dev.of_node, "cell-index",
&cell_index))
master->bus_num = cell_index;
}
/* we support all 4 SPI modes and LSB first option */
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST | SPI_CS_WORD;
master->set_cs = orion_spi_set_cs;
master->transfer_one = orion_spi_transfer_one;
master->num_chipselect = ORION_NUM_CHIPSELECTS;
master->setup = orion_spi_setup;
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
master->auto_runtime_pm = true;
master->use_gpio_descriptors = true;
master->flags = SPI_MASTER_GPIO_SS;
platform_set_drvdata(pdev, master);
spi = spi_master_get_devdata(master);
spi->master = master;
spi->dev = &pdev->dev;
devdata = device_get_match_data(&pdev->dev);
devdata = devdata ? devdata : &orion_spi_dev_data;
spi->devdata = devdata;
spi->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(spi->clk)) {
status = PTR_ERR(spi->clk);
goto out;
}
status = clk_prepare_enable(spi->clk);
if (status)
goto out;
/* The following clock is only used by some SoCs */
spi->axi_clk = devm_clk_get(&pdev->dev, "axi");
if (PTR_ERR(spi->axi_clk) == -EPROBE_DEFER) {
status = -EPROBE_DEFER;
goto out_rel_clk;
}
if (!IS_ERR(spi->axi_clk))
clk_prepare_enable(spi->axi_clk);
tclk_hz = clk_get_rate(spi->clk);
/*
* With old device tree, armada-370-spi could be used with
* Armada XP, however for this SoC the maximum frequency is
* 50MHz instead of tclk/4. On Armada 370, tclk cannot be
* higher than 200MHz. So, in order to be able to handle both
* SoCs, we can take the minimum of 50MHz and tclk/4.
*/
if (of_device_is_compatible(pdev->dev.of_node,
"marvell,armada-370-spi"))
master->max_speed_hz = min(devdata->max_hz,
DIV_ROUND_UP(tclk_hz, devdata->min_divisor));
else if (devdata->min_divisor)
master->max_speed_hz =
DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
else
master->max_speed_hz = devdata->max_hz;
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi->base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(spi->base)) {
status = PTR_ERR(spi->base);
goto out_rel_axi_clk;
}
for_each_available_child_of_node(pdev->dev.of_node, np) {
struct orion_direct_acc *dir_acc;
u32 cs;
/* Get chip-select number from the "reg" property */
status = of_property_read_u32(np, "reg", &cs);
if (status) {
dev_err(&pdev->dev,
"%pOF has no valid 'reg' property (%d)\n",
np, status);
continue;
}
/*
* Check if an address is configured for this SPI device. If
* not, the MBus mapping via the 'ranges' property in the 'soc'
* node is not configured and this device should not use the
* direct mode. In this case, just continue with the next
* device.
*/
status = of_address_to_resource(pdev->dev.of_node, cs + 1, r);
if (status)
continue;
/*
* Only map one page for direct access. This is enough for the
* simple TX transfer which only writes to the first word.
* This needs to get extended for the direct SPI NOR / SPI NAND
* support, once this gets implemented.
*/
dir_acc = &spi->child[cs].direct_access;
dir_acc->vaddr = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
if (!dir_acc->vaddr) {
status = -ENOMEM;
of_node_put(np);
goto out_rel_axi_clk;
}
dir_acc->size = PAGE_SIZE;
dev_info(&pdev->dev, "CS%d configured for direct access\n", cs);
}
pm_runtime_set_active(&pdev->dev);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
pm_runtime_enable(&pdev->dev);
status = orion_spi_reset(spi);
if (status < 0)
goto out_rel_pm;
master->dev.of_node = pdev->dev.of_node;
status = spi_register_master(master);
if (status < 0)
goto out_rel_pm;
return status;
out_rel_pm:
pm_runtime_disable(&pdev->dev);
out_rel_axi_clk:
clk_disable_unprepare(spi->axi_clk);
out_rel_clk:
clk_disable_unprepare(spi->clk);
out:
spi_master_put(master);
return status;
}