Source
x
#define HSSPI_SPI_MAX_CS 8
#define HSSPI_BUS_NUM 1 /* 0 is legacy SPI */
struct bcm63xx_hsspi {
struct completion done;
struct mutex bus_mutex;
struct platform_device *pdev;
struct clk *clk;
struct clk *pll_clk;
void __iomem *regs;
u8 __iomem *fifo;
u32 speed_hz;
u8 cs_polarity;
};
static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned int cs,
bool active)
{
return IRQ_HANDLED;
}
static int bcm63xx_hsspi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct bcm63xx_hsspi *bs;
struct resource *res_mem;
void __iomem *regs;
struct device *dev = &pdev->dev;
struct clk *clk;
struct clk *clk, *pll_clk = NULL;
int irq, ret;
u32 reg, rate, num_cs = HSSPI_SPI_MAX_CS;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "no irq: %d\n", irq);
return irq;
}
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (IS_ERR(clk))
return PTR_ERR(clk);
ret = clk_prepare_enable(clk);
if (ret)
return ret;
rate = clk_get_rate(clk);
if (!rate) {
struct clk *pll_clk = devm_clk_get(dev, "pll");
pll_clk = devm_clk_get(dev, "pll");
if (IS_ERR(pll_clk)) {
ret = PTR_ERR(pll_clk);
goto out_disable_clk;
}
ret = clk_prepare_enable(pll_clk);
if (ret)
goto out_disable_clk;
rate = clk_get_rate(pll_clk);
clk_disable_unprepare(pll_clk);
if (!rate) {
ret = -EINVAL;
goto out_disable_clk;
goto out_disable_pll_clk;
}
}
master = spi_alloc_master(&pdev->dev, sizeof(*bs));
if (!master) {
ret = -ENOMEM;
goto out_disable_clk;
goto out_disable_pll_clk;
}
bs = spi_master_get_devdata(master);
bs->pdev = pdev;
bs->clk = clk;
bs->pll_clk = pll_clk;
bs->regs = regs;
bs->speed_hz = rate;
bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0));
mutex_init(&bs->bus_mutex);
init_completion(&bs->done);
master->dev.of_node = dev->of_node;
if (!dev->of_node)
master->bus_num = HSSPI_BUS_NUM;
/* register and we are done */
ret = devm_spi_register_master(dev, master);
if (ret)
goto out_put_master;
return 0;
out_put_master:
spi_master_put(master);
out_disable_pll_clk:
clk_disable_unprepare(pll_clk);
out_disable_clk:
clk_disable_unprepare(clk);
return ret;
}
static int bcm63xx_hsspi_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
/* reset the hardware and block queue progress */
__raw_writel(0, bs->regs + HSSPI_INT_MASK_REG);
clk_disable_unprepare(bs->pll_clk);
clk_disable_unprepare(bs->clk);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int bcm63xx_hsspi_suspend(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
spi_master_suspend(master);
clk_disable_unprepare(bs->pll_clk);
clk_disable_unprepare(bs->clk);
return 0;
}
static int bcm63xx_hsspi_resume(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
int ret;
ret = clk_prepare_enable(bs->clk);
if (ret)
return ret;
if (bs->pll_clk) {
ret = clk_prepare_enable(bs->pll_clk);
if (ret)
return ret;
}
spi_master_resume(master);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(bcm63xx_hsspi_pm_ops, bcm63xx_hsspi_suspend,
bcm63xx_hsspi_resume);
static const struct of_device_id bcm63xx_hsspi_of_match[] = {