| /* |
| <:copyright-BRCM:2013:DUAL/GPL:standard |
| |
| Copyright (c) 2013 Broadcom Corporation |
| All Rights Reserved |
| |
| Unless you and Broadcom execute a separate written software license |
| agreement governing use of this software, this software is licensed |
| to you under the terms of the GNU General Public License version 2 |
| (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| with the following added to such license: |
| |
| As a special exception, the copyright holders of this software give |
| you permission to link this software with independent modules, and |
| to copy and distribute the resulting executable under terms of your |
| choice, provided that you also meet, for each linked independent |
| module, the terms and conditions of the license of that module. |
| An independent module is a module which is not derived from this |
| software. The special exception does not apply to any modifications |
| of the software. |
| |
| Not withstanding the above, under no circumstances may you combine |
| this software in any way with any other Broadcom software provided |
| under a license other than the GPL, without Broadcom's express prior |
| written consent. |
| |
| :> |
| |
| */ |
| |
| #include "bbsi.h" |
| #include <linux/spi/spi.h> |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) |
| #else |
| typedef unsigned long uintptr_t; |
| #endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) |
| |
| #define MOCA_RD(x) ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \ |
| (*((volatile uint32_t *)((unsigned long)(x)))) : \ |
| ((uint32_t)kerSysBcmSpiSlaveReadReg32(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (uint32_t)(x)))) |
| |
| #define MOCA_RD8(x, y) ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \ |
| (*(y) = *((volatile unsigned char *)((unsigned long)(x)))) : \ |
| (kerSysBcmSpiSlaveRead(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (unsigned long)(x), y, 1))) |
| |
| #define MOCA_WR(x,y) do { ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \ |
| (*((volatile uint32_t *)((unsigned long)(x)))) = (y) : \ |
| kerSysBcmSpiSlaveWriteReg32(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (uint32_t)(x), (y))); } while(0) |
| |
| #define MOCA_WR8(x,y) do { ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \ |
| (*((volatile unsigned char *)((unsigned long)(x)))) = (unsigned char)(y) : \ |
| kerSysBcmSpiSlaveWrite(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (unsigned long)(x), (y), 1)); } while(0) |
| |
| #define MOCA_WR16(x,y) do { ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \ |
| (*((volatile unsigned short *)((unsigned long)(x)))) = (unsigned short)(y) : \ |
| kerSysBcmSpiSlaveWrite(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (unsigned long)(x), (y), 2)); } while(0) |
| |
| #define MOCA_WR_BLOCK(addr, src, len) do { kerSysBcmSpiSlaveWriteBuf(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, addr, src, len, 4); } while(0) |
| #define MOCA_RD_BLOCK(addr, dst, len) do { kerSysBcmSpiSlaveReadBuf(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, addr, dst, len, 4); } while(0) |
| |
| |
| #define I2C_RD(x) MOCA_RD(x) |
| #define I2C_WR(x, y) MOCA_WR(x, y) |
| |
| #define MOCA_BPCM_NUM 5 |
| #define MOCA_BPCM_ZONES_NUM 8 |
| |
| #define MOCA_CPU_CLOCK_NUM 1 |
| #define MOCA_PHY_CLOCK_NUM 2 |
| |
| typedef enum _PMB_COMMAND_E_ |
| { |
| PMB_COMMAND_PHY1_ON=0, |
| PMB_COMMAND_PARTIAL_ON, |
| PMB_COMMAND_PHY1_OFF, |
| PMB_COMMAND_ALL_OFF, |
| |
| PMB_COMMAND_LAST |
| } PMB_COMMAND_E; |
| |
| typedef enum _PMB_GIVE_OWNERSHIP_E_ |
| { |
| PMB_GIVE_OWNERSHIP_2_HOST = 0, |
| PMB_GIVE_OWNERSHIP_2_FW, |
| |
| PMB_GET_OWNERSHIP_LAST |
| } PMB_GIVE_OWNERSHIP_E; |
| |
| struct moca_680x_clk |
| { |
| struct device *dev; |
| uint32_t clock_num; |
| }; |
| |
| static uint32_t zone_all_off_bitmask[MOCA_BPCM_NUM] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
| static uint32_t zone_partial_on_bitmask[MOCA_BPCM_NUM] = { 0x41, 0xFC, 0xFF, 0xFF, 0x00 }; |
| static uint32_t zone_phy1_bitmask[MOCA_BPCM_NUM] = { 0x00, 0x00, 0x00, 0x00, 0xFF }; |
| |
| |
| static void bogus_release(struct device *dev) |
| { |
| } |
| |
| static struct moca_platform_data moca_lan_data = { |
| .macaddr_hi = 0x00000102, |
| .macaddr_lo = 0x03040000, |
| |
| .bcm3450_i2c_base = 0x10406200, |
| .bcm3450_i2c_addr = 0x70, |
| .hw_rev = HWREV_MOCA_20_GEN22, |
| .rf_band = MOCA_BAND_EXT_D, |
| .chip_id = 0, |
| .use_dma = 0, |
| .use_spi = 1, |
| .devId = MOCA_DEVICE_ID_UNREGISTERED, // Filled in dynamically |
| #ifdef CONFIG_SMP |
| .smp_processor_id = 1, |
| #endif |
| }; |
| |
| static struct resource moca_lan_resources[] = { |
| [0] = { |
| .start = 0x10600000, |
| .end = 0x107ffd97, |
| .flags = IORESOURCE_MEM, |
| }, |
| [1] = { /* Not used for 6802, define for bmoca */ |
| .start = 0, |
| .end = 0, |
| .flags = IORESOURCE_IRQ, |
| } |
| }; |
| |
| static struct platform_device moca_lan_plat_dev = { |
| .name = "bmoca", |
| .id = 0, |
| .num_resources = ARRAY_SIZE(moca_lan_resources), |
| .resource = moca_lan_resources, |
| .dev = { |
| .platform_data = &moca_lan_data, |
| .release = bogus_release, |
| }, |
| }; |
| |
| static struct moca_platform_data moca_wan_data = { |
| .macaddr_hi = 0x00000102, |
| .macaddr_lo = 0x03040000, |
| |
| .bcm3450_i2c_base = 0x10406200, |
| .bcm3450_i2c_addr = 0x70, |
| .hw_rev = HWREV_MOCA_20_GEN22, |
| .chip_id = 0, |
| |
| .rf_band = MOCA_BAND_EXT_D, |
| |
| .use_dma = 0, |
| .use_spi = 1, |
| .devId = MOCA_DEVICE_ID_UNREGISTERED, // Filled in dynamically |
| |
| #ifdef CONFIG_SMP |
| .smp_processor_id = 1, |
| #endif |
| }; |
| |
| static struct resource moca_wan_resources[] = { |
| [0] = { |
| .start = 0x10600000, |
| .end = 0x107ffd97, |
| .flags = IORESOURCE_MEM, |
| }, |
| [1] = { /* Not used for 6802, define for bmoca */ |
| .start = 0, |
| .end = 0, |
| .flags = IORESOURCE_IRQ, |
| } |
| }; |
| |
| static struct platform_device moca_wan_plat_dev = { |
| .name = "bmoca", |
| .id = 1, |
| .num_resources = ARRAY_SIZE(moca_wan_resources), |
| .resource = moca_wan_resources, |
| .dev = { |
| .platform_data = &moca_wan_data, |
| .release = bogus_release, |
| }, |
| }; |
| |
| static void moca_enable_irq(struct moca_priv_data *priv) |
| { |
| kerSysMocaHostIntrEnable(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi); |
| } |
| |
| static void moca_disable_irq(struct moca_priv_data *priv) |
| { |
| kerSysMocaHostIntrDisable(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi); |
| } |
| |
| static void moca_pmb_busy_wait(struct moca_priv_data *priv) |
| { |
| #if 0 |
| uint32_t data; |
| |
| /* Possible time saver: The register access time over SPI may |
| always be enough to guarantee that the write will complete |
| in time without having to check the status. */ |
| do |
| { |
| data = MOCA_RD(priv->base + priv->regs->pmb_master_status); |
| } while (data & 0x1); |
| #endif |
| } |
| |
| void moca_pmb_delay(struct moca_priv_data *priv) |
| { |
| unsigned int data; |
| int i, j; |
| |
| MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, 0xFF444000); |
| |
| for (i = 0; i < MOCA_BPCM_NUM; i++) |
| { |
| for (j = 0; j < MOCA_BPCM_ZONES_NUM; j++) |
| { |
| data = 0x100012 + j*4 + i*0x1000; ; |
| MOCA_WR(priv->base + priv->regs->pmb_master_cmd_offset, data); |
| moca_pmb_busy_wait(priv); |
| } |
| } |
| } |
| |
| static void moca_pmb_control(struct moca_priv_data *priv, PMB_COMMAND_E cmd) |
| { |
| int i, j; |
| uint32_t * p_zone_control; |
| uint32_t data; |
| |
| switch (cmd) |
| { |
| case PMB_COMMAND_ALL_OFF: |
| // Turn off zone command |
| MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, 0xA00); |
| p_zone_control = &zone_all_off_bitmask[0]; |
| break; |
| |
| case PMB_COMMAND_PHY1_OFF: |
| // Turn off zone command |
| MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, 0xA00); |
| p_zone_control = &zone_phy1_bitmask[0]; |
| break; |
| |
| case PMB_COMMAND_PHY1_ON: |
| // Turn on zone command |
| MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, 0xC00); |
| p_zone_control = &zone_phy1_bitmask[0]; |
| break; |
| |
| case PMB_COMMAND_PARTIAL_ON: |
| // Turn on zone command |
| MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, 0xC00); |
| p_zone_control = &zone_partial_on_bitmask[0]; |
| break; |
| |
| |
| default: |
| printk(KERN_WARNING "%s: illegal cmd: %08x\n", |
| __FUNCTION__, cmd); |
| return; |
| } |
| |
| for (i = 0; i < MOCA_BPCM_NUM; i++) |
| { |
| for (j = 0; j < MOCA_BPCM_ZONES_NUM; j++) |
| { |
| if (*p_zone_control & (1 << j)) |
| { |
| // zone address in bpcms |
| data = (0x1 << 20) + 16 + (i * 4096) + (j * 4); |
| MOCA_WR(priv->base + priv->regs->pmb_master_cmd_offset, data); |
| moca_pmb_busy_wait(priv); |
| } |
| } |
| p_zone_control++; |
| } |
| |
| } |
| |
| static void moca_pmb_give_cntrl(struct moca_priv_data *priv, PMB_GIVE_OWNERSHIP_E cmd) |
| { |
| int i; |
| uint32_t data; |
| |
| /* Pass control over the memories to the FW */ |
| MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, cmd); |
| for (i = 0; i < 3; i++) |
| { |
| data = 0x100002 + i*0x1000; |
| MOCA_WR(priv->base + priv->regs->pmb_master_cmd_offset, data); |
| moca_pmb_busy_wait(priv); |
| } |
| moca_pmb_busy_wait(priv); |
| } |
| |
| static void moca_hw_reset(struct moca_priv_data *priv) |
| { |
| // unsigned long flags; |
| // uint32_t chipid; |
| |
| |
| /* disable and clear all interrupts */ |
| MOCA_WR(priv->base + priv->regs->l2_mask_set_offset, 0xffffffff); |
| MOCA_RD(priv->base + priv->regs->l2_mask_set_offset); |
| |
| /* assert resets */ |
| |
| /* reset CPU first, both CPUs for MoCA 20 HW */ |
| if (priv->hw_rev == HWREV_MOCA_20_GEN22) |
| MOCA_SET(priv->base + priv->regs->sw_reset_offset, 5); |
| else |
| MOCA_SET(priv->base + priv->regs->sw_reset_offset, 1); |
| |
| MOCA_RD(priv->base + priv->regs->sw_reset_offset); |
| |
| udelay(20); |
| |
| /* reset everything else except clocks */ |
| MOCA_SET(priv->base + priv->regs->sw_reset_offset, |
| ~((1 << 3) | (1 << 7) | (1 << 15) | (1 << 16))); |
| MOCA_RD(priv->base + priv->regs->sw_reset_offset); |
| |
| udelay(20); |
| |
| /* disable clocks */ |
| MOCA_SET(priv->base + priv->regs->sw_reset_offset, |
| ~((1 << 3) | (1 << 15) | (1 << 16))); |
| MOCA_RD(priv->base + priv->regs->sw_reset_offset); |
| |
| MOCA_WR(priv->base + priv->regs->l2_clear_offset, 0xffffffff); |
| MOCA_RD(priv->base + priv->regs->l2_clear_offset); |
| |
| /* Power down all zones */ |
| // The host can't give to itself permission. |
| moca_pmb_control(priv, PMB_COMMAND_ALL_OFF); |
| |
| /* Power down all SYS_CTRL memories */ |
| MOCA_WR(0x10100068, 1); // CLKGEN_PLL_SYS1_PLL_PWRDN |
| MOCA_SET(0x1010000c, 1); // CLKGEN_PLL_SYS0_PLL_CHANNEL_CTRL_CH_3 |
| |
| } |
| |
| static unsigned int moca_get_phy_freq(struct moca_priv_data *priv) |
| { |
| unsigned int x = MOCA_RD(0x10100044); // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_2 |
| |
| x = (x >> 1) & 0xFF; // Get the MDIV_CH2 field |
| |
| return( x ? 2400 / x : 0); |
| } |
| |
| |
| static void moca_ps_PowerCtrlPHY1(struct moca_priv_data *priv, PMB_COMMAND_E cmd) |
| { |
| uint32_t pll_ctrl_3, pll_ctrl_5, sw_reset; |
| pll_ctrl_3 = MOCA_RD (0x10100048); |
| pll_ctrl_5 = MOCA_RD (0x10100050); |
| sw_reset = MOCA_RD (priv->base + priv->regs->sw_reset_offset); |
| |
| // enable PLL |
| MOCA_UNSET(0x10100048, 1); // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_3 |
| MOCA_UNSET(0x10100050, 1); // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_5 |
| |
| udelay(1); |
| |
| // de assert moca_phy1_disable_clk |
| MOCA_UNSET(priv->base + priv->regs->sw_reset_offset, (1 << 9)); |
| |
| moca_pmb_control(priv, cmd); |
| |
| MOCA_WR (0x10100048, pll_ctrl_3); |
| MOCA_WR (0x10100050, pll_ctrl_5); |
| |
| udelay(1); |
| |
| MOCA_WR (priv->base + priv->regs->sw_reset_offset, sw_reset); |
| } |
| |
| |
| static void moca_gphy_init(struct moca_priv_data *priv) |
| { |
| struct moca_platform_data * pMocaData = (struct moca_platform_data *)priv->pdev->dev.platform_data; |
| u32 port_mode; |
| u32 rgmii0_on; |
| u32 rgmii1_on; |
| u32 gphy_enabled = 0; |
| |
| port_mode = MOCA_RD(0x10800000) & 0x3; |
| rgmii0_on = MOCA_RD(0x1080000c) & 0x1; |
| rgmii1_on = MOCA_RD(0x10800018) & 0x1; |
| |
| if ((pMocaData->chip_id & 0xFFFEFFF0) == 0x680200C0) |
| { |
| if ((port_mode == 0) || |
| ((port_mode == 1) && rgmii0_on) || |
| ((port_mode == 2) && rgmii1_on)) |
| { |
| gphy_enabled = 1; |
| } |
| } |
| else |
| { |
| if ((port_mode == 0) || |
| ((port_mode != 3) && rgmii1_on)) |
| { |
| gphy_enabled = 1; |
| } |
| } |
| |
| if (gphy_enabled) |
| { |
| MOCA_UNSET(0x10800004, 0xF); |
| msleep(10); |
| MOCA_WR(0x1040431c, 0xFFFFFFFF); |
| } |
| } |
| |
| /* called any time we start/restart/stop MoCA */ |
| static void moca_hw_init(struct moca_priv_data *priv, int action) |
| { |
| u32 mask; |
| u32 temp; |
| u32 data; |
| u32 count = 0; |
| struct moca_platform_data * pMocaData = (struct moca_platform_data *)priv->pdev->dev.platform_data; |
| |
| if (action == MOCA_ENABLE && !priv->enabled) { |
| clk_enable(priv->clk); |
| |
| MOCA_WR(0x1040431c, ~(1 << 26)); // SUN_TOP_CTRL_SW_INIT_0_CLEAR --> Do this at start of sequence, don't touch gphy_sw_init |
| udelay(20); |
| moca_gphy_init(priv); |
| |
| priv->enabled = 1; |
| } |
| |
| /* clock not enabled, register accesses will fail with bus error */ |
| if (!priv->enabled) |
| return; |
| |
| moca_hw_reset(priv); |
| udelay(1); |
| |
| if (action == MOCA_ENABLE) { |
| |
| /* Power up all zones */ |
| moca_pmb_control(priv, PMB_COMMAND_PARTIAL_ON); |
| |
| MOCA_UNSET(0x1010000c, 1); // CLKGEN_PLL_SYS0_PLL_CHANNEL_CTRL_CH_3 |
| |
| MOCA_WR(0x1010006C, 1); // CLKGEN_PLL_SYS1_PLL_RESET |
| MOCA_WR(0x10100068, 0); // CLKGEN_PLL_SYS1_PLL_PWRDN |
| data = 0; |
| while ((data & 0x1) == 0) |
| { |
| /* This typically is only read once */ |
| data = MOCA_RD(0x10100060); // CLKGEN_PLL_SYS1_PLL_LOCK_STATUS |
| |
| if (count++ > 10) |
| break; |
| } |
| MOCA_WR(0x1010006C, 0); // CLKGEN_PLL_SYS1_PLL_RESET |
| |
| if (priv->bonded_mode) { |
| MOCA_UNSET(0x10100048, 1); // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_3 |
| MOCA_UNSET(0x10100050, 1); // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_5 |
| } else { |
| MOCA_SET(0x10100048, 1); // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_3 |
| MOCA_SET(0x10100050, 1); // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_5 |
| } |
| udelay(1); |
| |
| /* deassert moca_sys_reset, system clock, phy0, phy0 clock */ |
| mask = (1 << 1) | (1 << 7) | (1 << 4) | (1 << 8); |
| |
| /* deassert phy1 and phy1 clock in bonded mode */ |
| if (priv->bonded_mode) |
| mask |= (1 << 5) | (1 << 9); |
| |
| MOCA_UNSET(priv->base + priv->regs->sw_reset_offset, mask); |
| MOCA_RD(priv->base + priv->regs->sw_reset_offset); |
| |
| // Before power off the memories, moca_phy1_disable_clk. |
| if (priv->bonded_mode==0) |
| moca_ps_PowerCtrlPHY1(priv, PMB_COMMAND_PHY1_OFF); |
| else |
| moca_ps_PowerCtrlPHY1(priv, PMB_COMMAND_PHY1_ON); |
| |
| |
| moca_pmb_give_cntrl(priv, PMB_GIVE_OWNERSHIP_2_FW); |
| |
| /* Check for 6802/6803 A0 chip only with Xtal mod */ |
| if ((pMocaData->chip_id & 0xFFFEFFFF) == 0x680200A0) |
| { |
| data = MOCA_RD(0x1040401c); |
| if ((data & 0x7) == 0x2) { |
| /* 25MHz */ |
| printk("MoCA running with 25MHz XTAL\n"); |
| MOCA_WR(priv->base + priv->regs->host2moca_mmp_outbox_0_offset, 1); |
| } else { |
| printk("MoCA == 50MHz XTAL\n"); |
| /* 50MHz clock change only */ |
| MOCA_WR(priv->base + priv->regs->host2moca_mmp_outbox_0_offset, 0); |
| //Note: The re-configuration is in NDIV_INT, not PDIV. |
| //`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_DIV (32'h10100058) [09:00] = 10Â’d48 |
| temp = MOCA_RD(0x10100058); |
| temp = (temp & 0xFFFFFC00) + 48; |
| MOCA_WR(0x10100058, temp); |
| |
| //`CLKGEN_REG_START + `CLKGEN_PLL_SYS0_PLL_DIV (32'h10100018) [09:00] = 10Â’d40 |
| temp = MOCA_RD(0x10100018); |
| temp = (temp & 0xFFFFFC00) + 40; |
| MOCA_WR(0x10100018, temp); |
| |
| //`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_4 (32'h1010004C) [08:01] = 8Â’d48 |
| temp = MOCA_RD(0x1010004c); |
| temp = (temp & 0xFFFFFE01) + (48 << 1); |
| MOCA_WR(0x1010004c, temp); |
| |
| //`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_5 (32'h10100050) [08:01] = 8Â’d48 |
| temp = MOCA_RD(0x10100050); |
| temp = (temp & 0xFFFFFE01) + (48 << 1); |
| MOCA_WR(0x10100050, temp); |
| |
| // Then Restart the PLL. |
| |
| //`CLKGEN_REG_START + `CLKGEN_PLL_SYS0_PLL_RESET (32'h1010002C) [0] = 1Â’b1 |
| MOCA_SET(0x1010002c, 1); |
| //`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_RESET (32'h1010006C) [0] = 1Â’b1 |
| MOCA_SET(0x1010006c, 1); |
| |
| udelay(1); |
| |
| //`CLKGEN_REG_START + `CLKGEN_PLL_SYS0_PLL_RESET (32'h1010002C) [0] = 1Â’b0 |
| MOCA_UNSET(0x1010002c, 1); |
| //`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_RESET (32'h1010006C) [0] = 1Â’b0 |
| MOCA_UNSET(0x1010006c, 1); |
| } |
| } |
| |
| // CLKGEN_PLL_SYS1_PLL_SSC_MODE_CONTROL_HIGH |
| data = MOCA_RD(0x10100070); |
| data = (data & 0xFFFF0000) | 0x7dd; |
| MOCA_WR(0x10100070, data); |
| |
| // CLKGEN_PLL_SYS1_PLL_SSC_MODE_CONTROL_LOW |
| data = MOCA_RD(0x10100074); |
| data = (data & 0xffc00000) | 0x3d71; |
| MOCA_WR(0x10100074, data); |
| |
| // CLKGEN_PLL_SYS1_PLL_SSC_MODE_CONTROL_LOW |
| MOCA_SET(0x10100074, (1 << 22)); |
| } |
| |
| |
| if (priv->hw_rev <= HWREV_MOCA_20_GEN21) { |
| /* clear junk out of GP0/GP1 */ |
| MOCA_WR(priv->base + priv->regs->gp0_offset, 0xffffffff); |
| MOCA_WR(priv->base + priv->regs->gp1_offset, 0x0); |
| /* set up activity LED for 50% duty cycle */ |
| MOCA_WR(priv->base + priv->regs->led_ctrl_offset, |
| 0x40004000); |
| } |
| |
| /* enable DMA completion interrupts */ |
| mask = M2H_REQ | M2H_RESP | M2H_ASSERT | M2H_WDT_CPU1 | |
| M2H_NEXTCHUNK | M2H_DMA; |
| |
| if (priv->hw_rev >= HWREV_MOCA_20_GEN21) |
| mask |= M2H_WDT_CPU0 | M2H_NEXTCHUNK_CPU0 | |
| M2H_REQ_CPU0 | M2H_RESP_CPU0 | M2H_ASSERT_CPU0; |
| |
| MOCA_WR(priv->base + priv->regs->ringbell_offset, 0); |
| MOCA_WR(priv->base + priv->regs->l2_mask_clear_offset, mask); |
| MOCA_RD(priv->base + priv->regs->l2_mask_clear_offset); |
| |
| |
| /* Set pinmuxing for MoCA interrupt and flow control */ |
| MOCA_UNSET(0x10404110, 0xF00000FF); |
| MOCA_SET(0x10404110, 0x10000022); |
| |
| /* Set pinmuxing for MoCA IIC control */ |
| if (((pMocaData->chip_id & 0xFFFFFFF0) == 0x680200C0) || |
| ((pMocaData->chip_id & 0xFFFFFFF0) == 0x680300C0)) |
| { |
| MOCA_UNSET(0x10404100, 0xFF); // pin muxing |
| MOCA_SET(0x10404100, 0x22); // pin muxing |
| } |
| |
| MOCA_WR(0x100b0318, 2); |
| |
| if (action == MOCA_DISABLE && priv->enabled) { |
| priv->enabled = 0; |
| clk_disable(priv->clk); |
| } |
| } |
| |
| static void moca_ringbell(struct moca_priv_data *priv, uint32_t mask) |
| { |
| MOCA_WR(priv->base + priv->regs->ringbell_offset, mask); |
| } |
| |
| static uint32_t moca_start_mips(struct moca_priv_data *priv, unsigned int cpu) |
| { |
| if (priv->hw_rev == HWREV_MOCA_20_GEN22) { |
| if (cpu == 1) |
| MOCA_UNSET(priv->base + priv->regs->sw_reset_offset, |
| (1 << 0)); |
| else { |
| moca_mmp_init(priv, 1); |
| MOCA_UNSET(priv->base + priv->regs->sw_reset_offset, |
| (1 << 2)); |
| } |
| } else |
| MOCA_UNSET(priv->base + priv->regs->sw_reset_offset, (1 << 0)); |
| MOCA_RD(priv->base + priv->regs->sw_reset_offset); |
| |
| return(0); |
| } |
| |
| static void moca_m2m_xfer(struct moca_priv_data *priv, |
| uint32_t dst, uint32_t src, uint32_t ctl) |
| { |
| uint32_t status; |
| |
| MOCA_WR(priv->base + priv->regs->m2m_src_offset, src); |
| MOCA_WR(priv->base + priv->regs->m2m_dst_offset, dst); |
| MOCA_WR(priv->base + priv->regs->m2m_status_offset, 0); |
| MOCA_RD(priv->base + priv->regs->m2m_status_offset); |
| MOCA_WR(priv->base + priv->regs->m2m_cmd_offset, ctl); |
| |
| do { |
| status = MOCA_RD(priv->base + priv->regs->m2m_status_offset); |
| } while(status == 0); |
| |
| } |
| |
| static void moca_write_mem(struct moca_priv_data *priv, |
| uint32_t dst_offset, void *src, unsigned int len) |
| { |
| struct moca_platform_data *pd = priv->pdev->dev.platform_data; |
| |
| if((dst_offset >= priv->regs->cntl_mem_offset+priv->regs->cntl_mem_size) || |
| ((dst_offset + len) > priv->regs->cntl_mem_offset+priv->regs->cntl_mem_size)) { |
| printk(KERN_WARNING "%s: copy past end of cntl memory: %08x\n", |
| __FUNCTION__, dst_offset); |
| return; |
| } |
| |
| if ( 1 == pd->use_dma ) |
| { |
| dma_addr_t pa; |
| |
| pa = dma_map_single(&priv->pdev->dev, src, len, DMA_TO_DEVICE); |
| mutex_lock(&priv->copy_mutex); |
| moca_m2m_xfer(priv, dst_offset + priv->regs->data_mem_offset, (uint32_t)pa, len | M2M_WRITE); |
| mutex_unlock(&priv->copy_mutex); |
| dma_unmap_single(&priv->pdev->dev, pa, len, DMA_TO_DEVICE); |
| } |
| else |
| { |
| uintptr_t addr = (uintptr_t)priv->base + priv->regs->data_mem_offset + dst_offset; |
| uint32_t *data = src; |
| int i; |
| |
| mutex_lock(&priv->copy_mutex); |
| if (((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 1) |
| { |
| src = data; |
| MOCA_WR_BLOCK(addr, src, len); |
| } |
| else |
| { |
| for(i = 0; i < len; i += 4, addr += 4, data++) |
| MOCA_WR(addr, *data); |
| MOCA_RD(addr - 4); /* flush write */ |
| } |
| |
| mutex_unlock(&priv->copy_mutex); |
| } |
| } |
| |
| static void moca_read_mem(struct moca_priv_data *priv, |
| void *dst, uint32_t src_offset, unsigned int len) |
| { |
| struct moca_platform_data *pd = priv->pdev->dev.platform_data; |
| |
| if((src_offset >= priv->regs->cntl_mem_offset+priv->regs->cntl_mem_size) || |
| ((src_offset + len) > priv->regs->cntl_mem_offset+priv->regs->cntl_mem_size)) { |
| printk(KERN_WARNING "%s: copy past end of cntl memory: %08x\n", |
| __FUNCTION__, src_offset); |
| return; |
| } |
| |
| if ( 1 == pd->use_dma ) |
| { |
| dma_addr_t pa; |
| |
| pa = dma_map_single(&priv->pdev->dev, dst, len, DMA_FROM_DEVICE); |
| mutex_lock(&priv->copy_mutex); |
| moca_m2m_xfer(priv, (uint32_t)pa, src_offset + priv->regs->data_mem_offset, len | M2M_READ); |
| mutex_unlock(&priv->copy_mutex); |
| dma_unmap_single(&priv->pdev->dev, pa, len, DMA_FROM_DEVICE); |
| } |
| else |
| { |
| uintptr_t addr = priv->regs->data_mem_offset + src_offset; |
| uint32_t *data = dst; |
| int i; |
| |
| mutex_lock(&priv->copy_mutex); |
| if (((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 1) |
| { |
| MOCA_RD_BLOCK((uintptr_t)priv->base + addr, dst, len); |
| } |
| else |
| { |
| for(i = 0; i < len; i += 4, addr += 4, data++) |
| *data = MOCA_RD((uintptr_t)priv->base + addr); |
| } |
| mutex_unlock(&priv->copy_mutex); |
| } |
| } |
| |
| static void moca_write_sg(struct moca_priv_data *priv, |
| uint32_t dst_offset, struct scatterlist *sg, int nents) |
| { |
| int j; |
| uintptr_t addr = priv->regs->data_mem_offset + dst_offset; |
| struct moca_platform_data *pd = priv->pdev->dev.platform_data; |
| |
| dma_map_sg(&priv->pdev->dev, sg, nents, DMA_TO_DEVICE); |
| |
| mutex_lock(&priv->copy_mutex); |
| for(j = 0; j < nents; j++) |
| { |
| if ( 1 == pd->use_dma ) |
| { |
| // printk("XXX copying page %d, PA %08x\n", j, (int)sg[j].dma_address); |
| moca_m2m_xfer(priv, addr, (uint32_t)sg[j].dma_address, |
| sg[j].length | M2M_WRITE); |
| |
| addr += sg[j].length; |
| } |
| else |
| { |
| unsigned long *data = (void *)phys_to_virt(sg[j].dma_address); |
| //printk("%s: Writing 0x%lx to addr 0x%08lx (len = %d)\n", __FUNCTION__, *data, ((unsigned long)priv->base) + addr, sg[j].length); |
| MOCA_WR_BLOCK(((unsigned long)priv->base) + addr, data, sg[j].length); |
| addr += sg[j].length; |
| } |
| } |
| mutex_unlock(&priv->copy_mutex); |
| |
| dma_unmap_sg(&priv->pdev->dev, sg, nents, DMA_TO_DEVICE); |
| } |
| |
| /* NOTE: this function is not tested */ |
| #if 0 |
| static void moca_read_sg(struct moca_priv_data *priv, |
| uint32_t src_offset, struct scatterlist *sg, int nents) |
| { |
| int j; |
| uintptr_t addr = priv->data_mem_offset + src_offset; |
| |
| dma_map_sg(&priv->pdev->dev, sg, nents, DMA_FROM_DEVICE); |
| |
| mutex_lock(&priv->copy_mutex); |
| for(j = 0; j < nents; j++) { |
| #if 0 //USE_DMA |
| printk("XXX copying page %d, PA %08x\n", j, (int)sg[j].dma_address); |
| moca_m2m_xfer(priv, addr, (uint32_t)sg[j].dma_address, |
| sg[j].length | M2M_READ); |
| |
| addr += sg[j].length; |
| #else |
| uint32_t *data = (void *)phys_to_virt(sg[j].dma_address); |
| unsigned int len = sg[j].length; |
| int i; |
| |
| for(i = 0; i < len; i += 4, addr += 4, data++) { |
| *data = cpu_to_be32( |
| MOCA_RD((uintptr_t)priv->base + addr)); |
| //printk("MoCA READ: AD 0x%x = 0x%x (0x%x)\n", (priv->base + addr), MOCA_RD((uintptr_t)priv->base + addr), *data); |
| } |
| #endif |
| } |
| mutex_unlock(&priv->copy_mutex); |
| |
| dma_unmap_sg(&priv->pdev->dev, sg, nents, DMA_FROM_DEVICE); |
| } |
| #endif |
| |
| static void moca_read_mac_addr(struct moca_priv_data *priv, uint32_t * hi, uint32_t * lo) |
| { |
| struct net_device * pdev ; |
| char mocaName[7] ; |
| |
| if (priv == NULL) |
| sprintf (mocaName, "moca%u", 0) ; |
| else |
| sprintf (mocaName, "moca%u", ((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId) ; |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) |
| pdev = dev_get_by_name ( &init_net, mocaName ) ; |
| #else |
| pdev = dev_get_by_name ( mocaName ) ; |
| #endif |
| |
| if ((pdev != NULL) && (lo != NULL) && (hi != NULL)) { |
| mac_to_u32(hi, lo, pdev->dev_addr); |
| } |
| } |
| |
| |
| #if defined(DSL_MOCA) |
| |
| /* |
| * This helper function was added to allow the enet driver to compile in |
| * consumer environment for 68xx profiles. |
| */ |
| void moca_get_fc_bits(void * arg, unsigned long *moca_fc_reg) |
| { |
| struct moca_priv_data * priv; |
| struct moca_platform_data * pMocaData; |
| unsigned long flags; |
| |
| if (arg == NULL) { |
| return; |
| } |
| |
| priv = (struct moca_priv_data *) arg; |
| pMocaData = (struct moca_platform_data *)priv->pdev->dev.platform_data; |
| |
| *moca_fc_reg = 0; |
| if (priv != NULL) |
| { |
| /* We can't read moca core regs unless the core's clocks are on. */ |
| spin_lock_irqsave(&priv->clock_lock, flags); |
| if (priv->running) { |
| *moca_fc_reg = MOCA_RD(priv->base+priv->regs->sideband_gmii_fc_offset); |
| } |
| spin_unlock_irqrestore(&priv->clock_lock, flags); |
| } |
| } |
| |
| #endif /* DSL_MOCA */ |
| |
| static int __devinit bmoca_spi_probe(struct spi_device *spi) { |
| // TODO(apenwarr): match one spi device to one moca device struct. |
| // I happen to know that right now the system only registers one of |
| // moca_lan or moca_wan, never both, and there is never more than |
| // one moca chip present on our systems, so this is okay for now. |
| uint32_t val = kerSysBcmSpiSlaveReadReg32(spi, 0x10404000); |
| pr_info("bmoca_spi_probe bus=%d chip_select=%d: id=%08x %s\n", |
| spi->master->bus_num, spi->chip_select, val, |
| val != 0 ? "yes" : "no"); |
| if (val == 0) return -ENODEV; |
| moca_lan_data.spi = spi; |
| moca_wan_data.spi = spi; |
| return 0; // success |
| } |
| |
| static int __devexit bmoca_spi_remove(struct spi_device *spi) { |
| pr_info("bmoca_spi_remove\n"); |
| if (moca_lan_data.spi == spi) moca_lan_data.spi = NULL; |
| if (moca_wan_data.spi == spi) moca_wan_data.spi = NULL; |
| return 0; // success |
| } |
| |
| static struct spi_driver bmoca_spi_driver = { |
| .driver = { |
| .name = "bmoca", |
| .owner = THIS_MODULE, |
| }, |
| .probe = bmoca_spi_probe, |
| .remove = __devexit_p(bmoca_spi_remove), |
| }; |
| |
| //extern void bcmenet_register_moca_fc_bits_cb(void cb(void *, unsigned long *), int isWan, void * arg); |
| |
| static void moca_mem_init_680xC0( struct moca_priv_data *priv ) |
| { |
| // De-assert reset (all memories are OFF by default Force_SP_off =1, Force_Rf_off =1) |
| MOCA_UNSET(priv->base + priv->regs->sw_reset_offset, ((1 << 15) | (1 << 16))); |
| |
| moca_pmb_delay(priv); |
| moca_pmb_control(priv, PMB_COMMAND_ALL_OFF); |
| |
| //Write Force_SP_on =0, Force_SP_off =0, Force_RF_on =0, Force_RF_off =0 |
| MOCA_UNSET(priv->base + 0x001ffd14, ((1 << 10) | (1 << 11))); |
| moca_pmb_control(priv, PMB_COMMAND_PARTIAL_ON); |
| } |
| |
| static int hw_specific_init( struct moca_priv_data *priv ) |
| { |
| #ifdef DSL_MOCA |
| struct moca_platform_data *pMocaData; |
| u32 port_mode; |
| |
| pMocaData = (struct moca_platform_data *)priv->pdev->dev.platform_data; |
| |
| /* fill in the hw_rev field */ |
| pMocaData->chip_id = MOCA_RD(0x10404004) + 0xA0; |
| pr_info("read moca chip id: %08x\n", pMocaData->chip_id); |
| if ((pMocaData->chip_id & 0xFFFE0000) != 0x68020000) { /* 6802 or 6803 */ |
| printk(KERN_ERR "bmoca: No MoCA chip found\n"); |
| return -EFAULT; |
| } |
| |
| MOCA_WR(0x1040431c, 0x0FFFFFFF); // SUN_TOP_CTRL_SW_INIT_0_CLEAR |
| MOCA_WR(0x104040a4, 0x01); // GENERAL_CTRL_NO_SCAN_0 |
| MOCA_WR(0x10404100, 0x11110011); // PIN_MUX_CTRL_0 |
| MOCA_WR(0x10404104, 0x11111111); // PIN_MUX_CTRL_1 |
| |
| /* The definition of PORT_MODE has changed from chip revision B0 to C0 |
| * */ |
| if ((pMocaData->chip_id & 0xFFFEFFF0) == 0x680200C0) |
| port_mode = 2; /* RGMII_1 <-> GPHY, RGMII_0 <-> MoCA */ |
| else |
| port_mode = 3; /* RGMII_0 <-> MoCA */ |
| |
| MOCA_WR(0x10800000, port_mode); // EMUX_CNTRL |
| MOCA_WR(0x1080000c, 0x11); // RGMII_0_CNTRL |
| MOCA_WR(0x10800014, 0xc0); // RGMII_0_RX_CLK_DELAY_CNTRL |
| |
| if (((pMocaData->chip_id & 0xFFFFFFF0) == 0x680200C0) || ((pMocaData->chip_id & 0xFFFFFFF0) == 0x680300C0)) |
| { |
| priv->i2c_base = NULL; |
| |
| /* Initialize 680x CO memory */ |
| moca_mem_init_680xC0(priv); |
| } |
| |
| pMocaData->hw_rev = HWREV_MOCA_20_GEN22; |
| |
| /* Power down all LEAP memories */ |
| MOCA_WR(0x101000e4, 0x6); // CLKGEN_LEAP_TOP_INST_DATA |
| MOCA_WR(0x101000e8, 0x6); // CLKGEN_LEAP_TOP_INST_HAB |
| MOCA_WR(0x101000ec, 0x6); // CLKGEN_LEAP_TOP_INST_PROG0 |
| MOCA_WR(0x101000f0, 0x6); // CLKGEN_LEAP_TOP_INST_PROG1 |
| MOCA_WR(0x101000f4, 0x6); // CLKGEN_LEAP_TOP_INST_PROG2 |
| MOCA_WR(0x101000f8, 0x6); // CLKGEN_LEAP_TOP_INST_ROM |
| MOCA_WR(0x101000fc, 0x6); // CLKGEN_LEAP_TOP_INST_SHARED |
| MOCA_WR(0x10100164, 0x3); // CLKGEN_SYS_CTRL_INST_POWER_SWITCH_MEMORY |
| |
| // bcmenet_register_moca_fc_bits_cb( |
| // moca_get_fc_bits, pMocaData->use_spi ? 1 : 0, (void *)priv); |
| #endif |
| |
| return 0; |
| } |
| |
| static int moca_platform_dev_register(void) |
| { |
| struct moca_platform_data *pMocaData; |
| struct platform_device *pPlatformDev; |
| BP_MOCA_INFO mocaInfo[BP_MOCA_MAX_NUM]; |
| int mocaChipNum = BP_MOCA_MAX_NUM; |
| int i; |
| int ret = 0; |
| |
| BpGetMocaInfo(mocaInfo, &mocaChipNum); |
| |
| ret = spi_register_driver(&bmoca_spi_driver); |
| if (ret < 0) return ret; |
| |
| for (i = 0; i < mocaChipNum; i++) { |
| switch (mocaInfo[i].type) { |
| case BP_MOCA_TYPE_WAN: |
| pMocaData = &moca_wan_data; |
| pPlatformDev = &moca_wan_plat_dev; |
| break; |
| |
| case BP_MOCA_TYPE_LAN: |
| pMocaData = &moca_lan_data; |
| pPlatformDev = &moca_lan_plat_dev; |
| break; |
| |
| default: |
| printk(KERN_ERR "bmoca: unrecognized MoCA type %d\n", |
| mocaInfo[i].type); |
| return(-1); |
| break; |
| } |
| |
| ret = platform_device_register(pPlatformDev); |
| if (ret < 0) { |
| spi_unregister_driver(&bmoca_spi_driver); |
| return(ret); |
| } |
| else { |
| pMocaData->devId = i; |
| |
| /* Map the board params RF Band to the bmoca.h value */ |
| switch (mocaInfo[i].rfBand) |
| { |
| case BP_MOCA_RF_BAND_D_LOW: |
| pMocaData->rf_band = MOCA_BAND_D_LOW; |
| break; |
| case BP_MOCA_RF_BAND_D_HIGH: |
| pMocaData->rf_band = MOCA_BAND_D_HIGH; |
| break; |
| case BP_MOCA_RF_BAND_EXT_D: |
| pMocaData->rf_band = MOCA_BAND_EXT_D; |
| break; |
| case BP_MOCA_RF_BAND_E: |
| pMocaData->rf_band = MOCA_BAND_E; |
| break; |
| case BP_MOCA_RF_BAND_F: |
| pMocaData->rf_band = MOCA_BAND_F; |
| break; |
| default: |
| /* Do nothing */ |
| break; |
| } |
| printk(KERN_INFO "bmoca: Found MoCA device %d/%d RF Band %d\n", |
| i, mocaChipNum, mocaInfo[i].rfBand); |
| } |
| } |
| |
| return(ret); |
| } |
| |
| static void moca_platform_dev_unregister(void) |
| { |
| spi_unregister_driver(&bmoca_spi_driver); |
| |
| if (moca_lan_data.devId != MOCA_DEVICE_ID_UNREGISTERED) |
| platform_device_unregister(&moca_lan_plat_dev); |
| |
| if (moca_wan_data.devId != MOCA_DEVICE_ID_UNREGISTERED) |
| platform_device_unregister(&moca_wan_plat_dev); |
| } |
| |
| static void moca_3450_write(struct moca_priv_data *priv, u8 addr, u32 data) |
| { |
| /* comment out for now. We don't use i2c on the 63268BHR board */ |
| #ifdef MOCA_3450_USE_I2C |
| if (((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) |
| bcm3450_write_reg(addr, data); |
| else |
| #endif |
| { |
| if (priv->i2c_base != NULL) |
| moca_3450_write_i2c(priv, addr, data); |
| } |
| } |
| |
| static u32 moca_3450_read(struct moca_priv_data *priv, u8 addr) |
| { |
| /* comment out for now. We don't use i2c on the 63268BHR board */ |
| #ifdef MOCA_3450_USE_I2C |
| if (((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) |
| return(bcm3450_read_reg(addr)); |
| else |
| #endif |
| { |
| if (priv->i2c_base != NULL) |
| return(moca_3450_read_i2c(priv, addr)); |
| else |
| return(0xffffffff); |
| } |
| } |
| |
| /* |
| * PM STUBS |
| */ |
| |
| struct clk *clk_get(struct device *dev, const char *id) |
| { |
| // We're not actually using the "struct clk" for anything |
| // We'll use our own structure |
| struct moca_680x_clk * pclk = kzalloc(sizeof(struct moca_680x_clk), GFP_KERNEL); |
| |
| pclk->dev = dev; |
| |
| if (!strcmp(id, "moca-cpu")) |
| pclk->clock_num = MOCA_CPU_CLOCK_NUM; |
| else if (!strcmp(id, "moca-phy")) |
| pclk->clock_num = MOCA_PHY_CLOCK_NUM; |
| else |
| { |
| kfree(pclk); |
| return(NULL); |
| } |
| |
| return((struct clk *)pclk); |
| } |
| |
| int clk_enable(struct clk *clk) |
| { |
| return 0; |
| } |
| |
| void clk_disable(struct clk *clk) |
| { |
| } |
| |
| void clk_put(struct clk *clk) |
| { |
| kfree((struct moca_680x_clk *)clk); |
| } |
| |
| struct moca_6802c0_clock_params |
| { |
| uint32_t cpu_hz; |
| uint32_t pdiv; |
| uint32_t ndiv; |
| uint32_t pll_mdivs[6]; |
| }; |
| |
| #define NUM_6802C0_CLOCK_OPTIONS 2 |
| struct moca_6802c0_clock_params moca_6802c0_clock_params[NUM_6802C0_CLOCK_OPTIONS] = |
| { |
| { // VCO of 2100, default |
| 420000000, // cpu_hz |
| 1, // pdiv |
| 42, // ndiv |
| {5, 21, 7, 7, 42, 42} // pll_mdivs[6] |
| }, |
| { // VCO of 2400 |
| 400000000, // cpu_hz |
| 1, // pdiv |
| 48, // ndiv |
| {6, 24, 8, 8, 48, 48} // pll_mdivs[6] |
| }, |
| }; |
| |
| int clk_set_rate(struct clk *clk, unsigned long rate) |
| { |
| // The MOCA_RD/MOCA_WR macros need a valid 'priv->pdev->dev' |
| static struct moca_priv_data dummy_priv; |
| static struct platform_device dummy_pd; |
| struct moca_priv_data *priv = &dummy_priv; |
| struct moca_680x_clk * pclk = (struct moca_680x_clk *) clk; |
| struct moca_platform_data * pMocaData = (struct moca_platform_data *)pclk->dev->platform_data; |
| struct moca_6802c0_clock_params * p_clock_data = &moca_6802c0_clock_params[0]; |
| uint32_t i; |
| uint32_t addr; |
| uint32_t data; |
| int ret = -1; |
| |
| priv->pdev = &dummy_pd; |
| priv->pdev->dev = *pclk->dev; |
| |
| if (pclk->clock_num == MOCA_CPU_CLOCK_NUM) |
| { |
| if ((pMocaData->chip_id & 0xFFFFFFF0) == 0x680200C0) |
| { |
| for (i = 0; i < NUM_6802C0_CLOCK_OPTIONS; i++) |
| { |
| if (moca_6802c0_clock_params[i].cpu_hz == rate) |
| { |
| p_clock_data = &moca_6802c0_clock_params[i]; |
| ret = 0; |
| } |
| } |
| |
| // 1. Set POST_DIVIDER_HOLD_CHx (bit [12] in each PLL_CHANNEL_CTRL_CH_x |
| // register) // this will zero the output channels |
| for (addr = 0x1010003c; addr <= 0x10100050; addr += 4) |
| { |
| MOCA_SET(addr, (1 << 12)); |
| } |
| |
| //2. Program new PDIV/NDIV value, this will lose lock and |
| // trigger a new PLL lock process for a new VCO frequency |
| MOCA_WR(0x10100058, ((p_clock_data->pdiv << 10) | p_clock_data->ndiv)); |
| |
| //3. Wait >10 usec for lock time // max lock time per data sheet is 460/Fref, |
| // Or alternatively monitor CLKGEN_PLL_SYS*_PLL_LOCK_STATUS to check if PLL has locked |
| data = 0; |
| i = 0; |
| while ((data & 0x1) == 0) |
| { |
| /* This typically is only read once */ |
| data = MOCA_RD(0x10100060); // CLKGEN_PLL_SYS1_PLL_LOCK_STATUS |
| |
| if (i++ > 10) |
| { |
| printk("MoCA SYS1 PLL NOT LOCKED!\n"); |
| break; |
| } |
| } |
| |
| //4. Configure new MDIV value along with set POST_DIVIDER_LOAD_EN_CHx |
| // (bit [13]=1, while keep bit[12]=1) in each PLL_CHANNEL_CTRL_CH_x register |
| i = 0; |
| for (addr = 0x1010003c; addr <= 0x10100050; addr += 4) |
| { |
| data = MOCA_RD(addr); |
| data |= (1 << 13); |
| data &= ~(0xFF << 1); |
| data |= (p_clock_data->pll_mdivs[i] << 1); |
| MOCA_WR(addr, data); |
| i++; |
| } |
| |
| //5. Clear bits [12] and bit [13] in each PLL_CHANNEL_CTRL_CH_x |
| for (addr = 0x1010003c; addr <= 0x10100050; addr += 4) |
| { |
| MOCA_UNSET(addr, ((1 << 13) | (1 << 12))); |
| } |
| |
| } |
| } |
| |
| return(ret); |
| } |
| |