From 4ae9ca69dce8dac4390af9cc6bcdcb1470612fd4 Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Thu, 3 Apr 2014 17:52:23 +0200 Subject: [PATCH] bcwc_pcie: Bits and pieced for IRQ, PLL and DDR Signed-off-by: Patrik Jakobsson --- bcwc_drv.h | 10 ++ bcwc_hw.c | 277 +++++++++++++++++++++++++++++++++++++++++++++++++++-- bcwc_hw.h | 2 + bcwc_reg.h | 57 +++++++++++ 4 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 bcwc_reg.h diff --git a/bcwc_drv.h b/bcwc_drv.h index d769c5b..42091fd 100644 --- a/bcwc_drv.h +++ b/bcwc_drv.h @@ -13,11 +13,17 @@ #define _PCWC_PCIE_H #include +#include "bcwc_reg.h" #define BCWC_PCI_DEV_IO 0 #define BCWC_PCI_DEV_MEM 2 #define BCWC_PCI_LINK_IO 4 +struct bcwc_reg { + u32 offset; + u32 value; +}; + struct bcwc_private { struct pci_dev *pdev; unsigned int dma_mask; @@ -32,6 +38,10 @@ struct bcwc_private { /* Hardware info */ u32 core_clk; + + /* DDR_PHY saved registers. Offsets need to be initialized somewhere */ + u32 ddr_phy_num_regs; + struct bcwc_reg ddr_reg_map[DDR_PHY_NUM_REGS]; }; #endif diff --git a/bcwc_hw.c b/bcwc_hw.c index 138bff6..f3e72fe 100644 --- a/bcwc_hw.c +++ b/bcwc_hw.c @@ -9,25 +9,292 @@ * */ +#include #include "bcwc_drv.h" #include "bcwc_hw.h" -#if 0 +/* Used after most PCI Link IO writes */ +inline void bcwc_hw_pci_post(struct bcwc_private *dev_priv) +{ + pci_write_config_dword(dev_priv->pdev, 0, 0x12345678); +} + static int bcwc_hw_set_core_clk(struct bcwc_private *dev_priv) { return 0; } -static int bcwc_hw_aspm_enable(struct bcwc_private *dev_priv) +static int bcwc_hw_s2_pll_reset(struct bcwc_private *dev_priv) { + BCWC_DEV_REG_WRITE(0x40, DDR_PHY_2C); + bcwc_hw_pci_post(dev_priv); + return 0; } -static int bcwc_hw_aspm_disable(struct bcwc_private *dev_priv) +static int bcwc_hw_s2_init_pcie_link(struct bcwc_private *dev_priv) { + u32 reg; + + BCWC_DEV_REG_WRITE(S2_PCIE_LINK_D000, 0x10); + bcwc_hw_pci_post(dev_priv); + + BCWC_DEV_REG_WRITE(S2_PCIE_LINK_D120, 0x1804); + bcwc_hw_pci_post(dev_priv); + + BCWC_DEV_REG_WRITE(S2_PCIE_LINK_D124, 0xac5800); + bcwc_hw_pci_post(dev_priv); + + BCWC_DEV_REG_WRITE(S2_PCIE_LINK_D120, 0x1804); + bcwc_hw_pci_post(dev_priv); + + reg = BCWC_DEV_REG_READ(S2_PCIE_LINK_D124); + if (reg == 0xac5800) { + } + return 0; } -#endif + +static int bcwc_hw_s2_pll_init(struct bcwc_private *dev_priv, u32 ddr_speed) +{ + u32 ref_clk_25; + u32 reg; + int retries = 0; + + ref_clk_25 = (BCWC_DEV_REG_READ(S2_PLL_STATUS_04) && 0x8); + + if (ref_clk_25) + dev_info(&dev_priv->pdev->dev, "Refclk: 25MHz\n"); + else + dev_info(&dev_priv->pdev->dev, "Refclk: 24MHz\n"); + + if (ddr_speed == 400) { + if (ref_clk_25) { + /* Ref clk 25 */ + BCWC_DEV_REG_WRITE(0x00400078, S2_PLL_CTRL_510); + bcwc_hw_pci_post(dev_priv); + BCWC_DEV_REG_WRITE(0x19280804, S2_PLL_CTRL_24); + } else { + /* Ref clk 24 */ + BCWC_DEV_REG_WRITE(0x03200000, S2_PLL_CTRL_20); + bcwc_hw_pci_post(dev_priv); + BCWC_DEV_REG_WRITE(0x14280603, S2_PLL_CTRL_24); + } + } else if (ddr_speed == 300) { + if (ref_clk_25) { + /* Ref clk 25 */ + BCWC_DEV_REG_WRITE(0x03200000, S2_PLL_CTRL_20); + bcwc_hw_pci_post(dev_priv); + BCWC_DEV_REG_WRITE(0x14280804, S2_PLL_CTRL_24); + } else { + /* Ref clk 24 */ + BCWC_DEV_REG_WRITE(0x00480078, S2_PLL_CTRL_510); + bcwc_hw_pci_post(dev_priv); + BCWC_DEV_REG_WRITE(0x19280c06, S2_PLL_CTRL_24); + } + } else if (ddr_speed == 200) { + if (ref_clk_25) { + /* Ref clk 25 */ + BCWC_DEV_REG_WRITE(0x03200000, S2_PLL_CTRL_20); + bcwc_hw_pci_post(dev_priv); + BCWC_DEV_REG_WRITE(0x14280c06, S2_PLL_CTRL_24); + } else { + /* Ref clk 24 */ + BCWC_DEV_REG_WRITE(0x00400078, S2_PLL_CTRL_510); + bcwc_hw_pci_post(dev_priv); + BCWC_DEV_REG_WRITE(0x19281008, S2_PLL_CTRL_24); + } + } else { + if (ddr_speed != 450) { + dev_err(&dev_priv->pdev->dev, + "Unsupported DDR speed %uMHz, using 450MHz\n", + ddr_speed); + } + ddr_speed = 450; + + if (ref_clk_25) { + /* Ref clk 25 */ + BCWC_DEV_REG_WRITE(0x04b00000, S2_PLL_CTRL_20); + bcwc_hw_pci_post(dev_priv); + BCWC_DEV_REG_WRITE(0x14280904, S2_PLL_CTRL_24); + } else { + /* Ref clk 24 */ + BCWC_DEV_REG_WRITE(0x0048007d, S2_PLL_CTRL_510); + bcwc_hw_pci_post(dev_priv); + BCWC_DEV_REG_WRITE(0x19280904, S2_PLL_CTRL_24); + } + } + + bcwc_hw_pci_post(dev_priv); + bcwc_hw_s2_pll_reset(dev_priv); + + dev_info(&dev_priv->pdev->dev, "Waiting for PLL to lock\n"); + + do { + reg = BCWC_DEV_REG_READ(S2_PLL_STATUS_0C) & 0x80; + udelay(10); + retries++; + } while (!reg && retries <= 10000); + + if (retries > 10000) { + dev_info(&dev_priv->pdev->dev, "Failed to lock PLL\n"); + return -EINVAL; + } else { + dev_info(&dev_priv->pdev->dev, "PLL is locked\n"); + } + + reg = BCWC_DEV_REG_READ(S2_PLL_STATUS_A8); + BCWC_DEV_REG_WRITE(reg | 0x1, S2_PLL_STATUS_A8); + bcwc_hw_pci_post(dev_priv); + udelay(10000); + + reg = BCWC_DEV_REG_READ(S2_PLL_STATUS_A8); + if (reg & 0x1) { + dev_info(&dev_priv->pdev->dev, "PLL is in bypass mode\n"); + } else { + dev_info(&dev_priv->pdev->dev, "PLL is in non-bypass mode\n"); + } + + return 0; +} + +static int bcwc_hw_s2_init_ddr_controller_soc(struct bcwc_private *dev_priv) +{ + u32 cmd; + u32 reg; + int ret; + + /* Read PCI config command register */ + ret = pci_read_config_dword(dev_priv->pdev, 4, &cmd); + if (!ret) { + dev_err(&dev_priv->pdev->dev, "Failed to read PCI config\n"); + return -EIO; + } + + if ((cmd & 0x07) == 0) { + dev_err(&dev_priv->pdev->dev, + "PCI link in illegal state, cfg_cmd_reg: 0x%x\n", cmd); + return -EIO; + } + + reg = BCWC_DEV_REG_READ(DDR_PHY_9C); + reg &= 0xfffffcff; + + BCWC_DEV_REG_WRITE(reg, DDR_PHY_9C); + bcwc_hw_pci_post(dev_priv); + + BCWC_DEV_REG_WRITE(reg | 0x300, DDR_PHY_9C); + bcwc_hw_pci_post(dev_priv); + + /* + * FIXME: Need to find out the correct DDR speed. + * Just using 200 MHz for now + */ + bcwc_hw_s2_pll_init(dev_priv, 200); + + /* FIXME: Unfinished */ + + return 0; +} + +static int bcwc_hw_save_ddr_phy_regs(struct bcwc_private *dev_priv) +{ + u32 reg, offset; + int i; + + if (dev_priv->ddr_phy_num_regs == 0) + return -ENOENT; + + for (i = 0; i < dev_priv->ddr_phy_num_regs; i++) { + offset = dev_priv->ddr_reg_map[i].offset; + reg = BCWC_DEV_REG_READ(offset + DDR_PHY_REG_BASE); + dev_priv->ddr_reg_map[i].value = reg; + } + + return 0; +} + +static int bcwc_hw_irq_init(struct bcwc_private *dev_priv) +{ + u32 num_channels, queue_size; + u32 reg; + int i, retries; + + BCWC_LINK_REG_WRITE(IRQ_IPC_NUM_CHAN, 0); + bcwc_hw_pci_post(dev_priv); + + BCWC_LINK_REG_WRITE(IRQ_IPC_QUEUE_SIZE, 0); + bcwc_hw_pci_post(dev_priv); + + BCWC_LINK_REG_WRITE(IRQ_REG_08, 0); + bcwc_hw_pci_post(dev_priv); + + BCWC_LINK_REG_WRITE(IRQ_REG_0C, 0); + bcwc_hw_pci_post(dev_priv); + + BCWC_LINK_REG_WRITE(IRQ_REG_10, 0); + bcwc_hw_pci_post(dev_priv); + + BCWC_LINK_REG_WRITE(IRQ_REG_14, 0); + bcwc_hw_pci_post(dev_priv); + + BCWC_LINK_REG_WRITE(IRQ_REG_18, 0); + bcwc_hw_pci_post(dev_priv); + + BCWC_LINK_REG_WRITE(IRQ_REG_1C, 0); + bcwc_hw_pci_post(dev_priv); + + BCWC_LINK_REG_WRITE(IRQ_REG_41024, 0xffffffff); + bcwc_hw_pci_post(dev_priv); + + /* + * Probably the IPC queue + * FIXME: Check if we can do 64bit writes on PCIe + */ + for (i = IRQ_REG_RANGE_START; i <= IRQ_REG_RANGE_END; i += 8) { + BCWC_LINK_REG_WRITE(0xffffff, i); + BCWC_LINK_REG_WRITE(0x000000, i + 4); + } + bcwc_hw_pci_post(dev_priv); + + BCWC_LINK_REG_WRITE(IRQ_REG_40008, 0x80000000); + bcwc_hw_pci_post(dev_priv); + + BCWC_LINK_REG_WRITE(IRQ_REG_40004, 0x1); + bcwc_hw_pci_post(dev_priv); + + + for (retries = 0; retries < 1000; retries++) { + reg = BCWC_LINK_REG_READ(IRQ_REG_40004); + if ((reg & 0xff) == 0xf0) + break; + udelay(10); + } + + if (retries >= 1000) { + dev_info(&dev_priv->pdev->dev, "Init failed! No wake signal\n"); + return -EIO; + } + + BCWC_LINK_REG_WRITE(0xffffffff, IRQ_REG_41024); + + num_channels = BCWC_LINK_REG_READ(IRQ_IPC_NUM_CHAN) + 1; + queue_size = BCWC_LINK_REG_READ(IRQ_IPC_QUEUE_SIZE); + + dev_info(&dev_priv->pdev->dev, + "Number of IPC channels: %u, queue size: %u\n", + num_channels, queue_size); + + if (num_channels > 32) { + dev_info(&dev_priv->pdev->dev, "Too many IPC channels: %u\n", + num_channels); + return -EIO; + } + + /* + allocate_device_memory(queue_size, &ret, 0); + */ +} static int bcwc_hw_irq_enable(struct bcwc_private *dev_priv) { @@ -81,7 +348,5 @@ static int bcwc_hw_power_off(struct bcwc_private *dev_priv) int bcwc_hw_init(struct bcwc_private *dev_priv) { - u32 reg; - return 0; } diff --git a/bcwc_hw.h b/bcwc_hw.h index 723247c..520403d 100644 --- a/bcwc_hw.h +++ b/bcwc_hw.h @@ -12,6 +12,8 @@ #ifndef _BCWC_HW_H #define _BCWC_HW_H +#include + #define BCWC_LINK_REG_READ(offset) ioread32(dev_priv->link_io + (offset)) #define BCWC_LINK_REG_WRITE(val, offset) iowrite32((val), \ dev_priv->link_io + (offset)) diff --git a/bcwc_reg.h b/bcwc_reg.h new file mode 100644 index 0000000..5a45309 --- /dev/null +++ b/bcwc_reg.h @@ -0,0 +1,57 @@ +/* + * Broadcom PCIe 1570 webcam driver + * Some of the register defines are taken from the crystalhd driver + * + * Copyright (C) 2014 Patrik Jakobsson (patrik.r.jakobsson@gmail.com) + * + * 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 published by + * the Free Software Foundation. + * + */ + +#ifndef _BCWC_REG_H +#define _BCWC_REG_H + +/* On iomem with pointer at 0x0fd0 */ +#define DDR_PHY_2C 0x2c +#define DDR_PHY_9C 0x9c + +#define S2_PCIE_LINK_D000 0xd000 +#define S2_PCIE_LINK_D120 0xd120 +#define S2_PCIE_LINK_D124 0xd124 + +#define DDR_PHY_REG_BASE 0x2800 +#define DDR_PHY_NUM_REGS 127 /* Found in AppleCamIn::Start() */ + +/* On iomem with pointer at ...fill me in... */ +#define S2_PLL_STATUS_04 0x04 +#define S2_PLL_STATUS_REFCLK (1 << 3) /* 1 = 25MHz, 0 = 24MHz */ + +#define S2_PLL_STATUS_0C 0x0c /* Register is called CMU_R_PLL_STS_MEMADDR */ +#define S2_PLL_STATUS_LOCKED (1 << 7) /* 1 = PLL locked, 0 = PLL not locked */ + +#define S2_PLL_STATUS_A8 0xa8 /* Bit 0 is PLL bypass mode (1 = bypass, 0 = non-bypass mode */ + +#define S2_PLL_CTRL_20 0x20 /* S2 PLL CLK REG 1 */ +#define S2_PLL_CTRL_24 0x24 /* S2 PLL CLK REG 2 */ +#define S2_PLL_CTRL_510 0x510 /* S2 PLL CLK REG 4 */ + +/* On iomem with pointer at 0x0ff0 (Bar 4: 1MB) */ +#define IRQ_IPC_NUM_CHAN 0xc3000 +#define IRQ_IPC_QUEUE_SIZE 0xc3004 +#define IRQ_REG_08 0xc3008 +#define IRQ_REG_0C 0xc300c +#define IRQ_REG_10 0xc3010 +#define IRQ_REG_14 0xc3014 +#define IRQ_REG_18 0xc3018 +#define IRQ_REG_1C 0xc301c +#define IRQ_REG_40004 0x40004 +#define IRQ_REG_40008 0x40008 +#define IRQ_REG_41000 0x41000 +#define IRQ_REG_41024 0x41024 + +#define IRQ_REG_RANGE_START 0x0128 +#define IRQ_REG_RANGE_END 0x0220 + +#endif