/* * Copyright (C) 2014 Sascha Hauer, Pengutronix * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define pr_fmt(fmt) "imx6sx-sdb: " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PFUZE100_DEVICEID 0x0 #define PFUZE100_REVID 0x3 #define PFUZE100_FABID 0x4 #define PFUZE100_SW1ABVOL 0x20 #define PFUZE100_SW1ABSTBY 0x21 #define PFUZE100_SW1ABCONF 0x24 #define PFUZE100_SW1CVOL 0x2e #define PFUZE100_SW1CSTBY 0x2f #define PFUZE100_SW1CCONF 0x32 #define PFUZE100_SW1ABC_SETP(x) ((x - 3000) / 250) #define PFUZE100_VGEN5CTL 0x70 /* set all switches APS in normal and PFM mode in standby */ static int imx6sx_sdb_setup_pmic_mode(struct i2c_client *client, int chip) { unsigned char offset, i, switch_num, value; if (!chip) { /* pfuze100 */ switch_num = 6; offset = 0x31; } else { /* pfuze200 */ switch_num = 4; offset = 0x38; } value = 0xc; if (i2c_write_reg(client, 0x23, &value, 1) < 0) return -EIO; for (i = 0; i < switch_num - 1; i++) if (i2c_write_reg(client, offset + i * 7, &value, 1) < 0) return -EIO; return 0; } static int imx6sx_sdb_setup_pmic_voltages(void) { unsigned char value, rev_id = 0; struct i2c_adapter *adapter = NULL; struct i2c_client client; int addr = -1, bus = 0; if (!of_machine_is_compatible("fsl,imx6sx-sdb")) return 0; /* I2C2 bus (2-1 = 1 in barebox numbering) */ bus = 0; /* PFUZE100 device address is 0x08 */ addr = 0x08; adapter = i2c_get_adapter(bus); if (!adapter) return -ENODEV; client.adapter = adapter; client.addr = addr; if (i2c_read_reg(&client, PFUZE100_DEVICEID, &value, 1) < 0) goto err_out; if (i2c_read_reg(&client, PFUZE100_REVID, &rev_id, 1) < 0) goto err_out; pr_info("Found PFUZE100! deviceid 0x%x, revid 0x%x\n", value, rev_id); if (imx6sx_sdb_setup_pmic_mode(&client, value & 0xf)) goto err_out; /* set SW1AB standby volatage 0.975V */ if (i2c_read_reg(&client, PFUZE100_SW1ABSTBY, &value, 1) < 0) goto err_out; value &= ~0x3f; value |= PFUZE100_SW1ABC_SETP(9750); if (i2c_write_reg(&client, PFUZE100_SW1ABSTBY, &value, 1) < 0) goto err_out; /* set SW1AB/VDDARM step ramp up time from 16us to 4us/25mV */ if (i2c_read_reg(&client, PFUZE100_SW1ABCONF, &value, 1) < 0) goto err_out; value &= ~0xc0; value |= 0x40; if (i2c_write_reg(&client, PFUZE100_SW1ABCONF, &value, 1) < 0) goto err_out; /* set SW1C standby volatage 0.975V */ if (i2c_read_reg(&client, PFUZE100_SW1CSTBY, &value, 1) < 0) goto err_out; value &= ~0x3f; value |= PFUZE100_SW1ABC_SETP(9750); if (i2c_write_reg(&client, PFUZE100_SW1CSTBY, &value, 1) < 0) goto err_out; /* set SW1C/VDDSOC step ramp up time to from 16us to 4us/25mV */ if (i2c_read_reg(&client, PFUZE100_SW1CCONF, &value, 1) < 0) goto err_out; value &= ~0xc0; value |= 0x40; if (i2c_write_reg(&client, PFUZE100_SW1CCONF, &value, 1) < 0) goto err_out; /* Enable power of VGEN5 3V3, needed for SD3 */ if (i2c_read_reg(&client, PFUZE100_VGEN5CTL, &value, 1) < 0) goto err_out; value &= ~0x1F; value |= 0x1F; if (i2c_write_reg(&client, PFUZE100_VGEN5CTL, &value, 1) < 0) goto err_out; return 0; err_out: pr_err("Setting up PMIC failed\n"); return -EIO; } fs_initcall(imx6sx_sdb_setup_pmic_voltages); int ar8031_phy_fixup(struct phy_device *phydev) { /* * Enable 1.8V(SEL_1P5_1P8_POS_REG) on * Phy control debug reg 0 */ phy_write(phydev, 0x1d, 0x1f); phy_write(phydev, 0x1e, 0x8); /* rgmii tx clock delay enable */ phy_write(phydev, 0x1d, 0x05); phy_write(phydev, 0x1e, 0x100); return 0; } #define PHY_ID_AR8031 0x004dd074 #define AR_PHY_ID_MASK 0xffffffff static int imx6sx_sdb_setup_fec(void) { void __iomem *gprbase = (void *)MX6_IOMUXC_BASE_ADDR + 0x4000; uint32_t val; struct clk *clk; phy_register_fixup_for_uid(PHY_ID_AR8031, AR_PHY_ID_MASK, ar8031_phy_fixup); /* Active high for ncp692 */ gpio_direction_output(IMX_GPIO_NR(4, 16), 1); clk = clk_lookup("enet_ptp_25m"); if (IS_ERR(clk)) goto err; clk_enable(clk); clk = clk_lookup("enet_ref"); if (IS_ERR(clk)) goto err; clk_enable(clk); clk = clk_lookup("enet2_ref_125m"); if (IS_ERR(clk)) goto err; clk_enable(clk); val = readl(gprbase + IOMUXC_GPR1); /* Use 125M anatop loopback REF_CLK1 for ENET1, clear gpr1[13], gpr1[17]*/ val &= ~(1 << 13); val &= ~(1 << 17); /* Use 125M anatop loopback REF_CLK1 for ENET2, clear gpr1[14], gpr1[18]*/ val &= ~(1 << 14); val &= ~(1 << 18); writel(val, gprbase + IOMUXC_GPR1); /* Enable the ENET power, active low */ gpio_direction_output(IMX_GPIO_NR(2, 6), 0); /* Reset AR8031 PHY */ gpio_direction_output(IMX_GPIO_NR(2, 7), 0); udelay(500); gpio_set_value(IMX_GPIO_NR(2, 7), 1); return 0; err: pr_err("Setting up DFEC\n"); return -EIO; } static int imx6sx_sdb_coredevices_init(void) { if (!of_machine_is_compatible("fsl,imx6sx-sdb")) return 0; imx6sx_sdb_setup_fec(); imx6_bbu_internal_mmc_register_handler("sd", "/dev/mmc3", BBU_HANDLER_FLAG_DEFAULT); return 0; } console_initcall(imx6sx_sdb_coredevices_init);