1 /* 2 * (C) Copyright 2011 3 * egnite GmbH <info@egnite.de> 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 /* 9 * Ethernut 5 power management support 10 * 11 * This board may be supplied via USB, IEEE 802.3af PoE or an 12 * auxiliary DC input. An on-board ATmega168 microcontroller, 13 * the so called power management controller or PMC, is used 14 * to select the supply source and to switch on and off certain 15 * energy consuming board components. This allows to reduce the 16 * total stand-by consumption to less than 70mW. 17 * 18 * The main CPU communicates with the PMC via I2C. When 19 * CONFIG_CMD_BSP is defined in the board configuration file, 20 * then the board specific command 'pwrman' becomes available, 21 * which allows to manually deal with the PMC. 22 * 23 * Two distinct registers are provided by the PMC for enabling 24 * and disabling specific features. This avoids the often seen 25 * read-modify-write cycle or shadow register requirement. 26 * Additional registers are available to query the board 27 * status and temperature, the auxiliary voltage and to control 28 * the green user LED that is integrated in the reset switch. 29 * 30 * Note, that the AVR firmware of the PMC is released under BSDL. 31 * 32 * For additional information visit the project home page at 33 * http://www.ethernut.de/ 34 */ 35 #include <common.h> 36 #include <asm/arch/at91sam9260.h> 37 #include <asm/arch/at91_common.h> 38 #include <asm/arch/gpio.h> 39 #include <asm/io.h> 40 #include <i2c.h> 41 42 #include "ethernut5_pwrman.h" 43 44 /* PMC firmware version */ 45 static int pwrman_major; 46 static int pwrman_minor; 47 48 /* 49 * Enable Ethernut 5 power management. 50 * 51 * This function must be called during board initialization. 52 * While we are using u-boot's I2C subsystem, it may be required 53 * to enable the serial port before calling this function, 54 * in particular when debugging is enabled. 55 * 56 * If board specific commands are not available, we will activate 57 * all board components. 58 */ 59 void ethernut5_power_init(void) 60 { 61 pwrman_minor = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_VERS); 62 pwrman_major = pwrman_minor >> 4; 63 pwrman_minor &= 15; 64 65 #ifndef CONFIG_CMD_BSP 66 /* Do not modify anything, if we do not have a known version. */ 67 if (pwrman_major == 2) { 68 /* Without board specific commands we enable all features. */ 69 i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA, ~PWRMAN_ETHRST); 70 i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS, PWRMAN_ETHRST); 71 } 72 #endif 73 } 74 75 /* 76 * Reset Ethernet PHY. 77 * 78 * This function allows the re-configure the PHY after 79 * changing its strap pins. 80 */ 81 void ethernut5_phy_reset(void) 82 { 83 /* Do not modify anything, if we do not have a known version. */ 84 if (pwrman_major != 2) 85 return; 86 87 /* 88 * Make sure that the Ethernet clock is enabled and the PHY reset 89 * is disabled for at least 100 us. 90 */ 91 i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA, PWRMAN_ETHCLK); 92 i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS, PWRMAN_ETHRST); 93 udelay(100); 94 95 /* 96 * LAN8710 strap pins are 97 * PA14 => PHY MODE0 98 * PA15 => PHY MODE1 99 * PA17 => PHY MODE2 => 111b all capable 100 * PA18 => PHY ADDR0 => 0b 101 */ 102 at91_set_pio_input(AT91_PIO_PORTA, 14, 1); 103 at91_set_pio_input(AT91_PIO_PORTA, 15, 1); 104 at91_set_pio_input(AT91_PIO_PORTA, 17, 1); 105 at91_set_pio_input(AT91_PIO_PORTA, 18, 0); 106 107 /* Activate PHY reset for 100 us. */ 108 i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA, PWRMAN_ETHRST); 109 udelay(100); 110 i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS, PWRMAN_ETHRST); 111 112 at91_set_pio_input(AT91_PIO_PORTA, 14, 1); 113 } 114 115 /* 116 * Output the firmware version we got during initialization. 117 */ 118 void ethernut5_print_version(void) 119 { 120 printf("%u.%u\n", pwrman_major, pwrman_minor); 121 } 122 123 /* 124 * All code below this point is optional and implements 125 * the 'pwrman' command. 126 */ 127 #ifdef CONFIG_CMD_BSP 128 129 /* Human readable names of PMC features */ 130 char *pwrman_feat[8] = { 131 "board", "vbin", "vbout", "mmc", 132 "rs232", "ethclk", "ethrst", "wakeup" 133 }; 134 135 /* 136 * Print all feature names, that have its related flags enabled. 137 */ 138 static void print_flagged_features(u8 flags) 139 { 140 int i; 141 142 for (i = 0; i < 8; i++) { 143 if (flags & (1 << i)) 144 printf("%s ", pwrman_feat[i]); 145 } 146 } 147 148 /* 149 * Return flags of a given list of feature names. 150 * 151 * The function stops at the first unknown list entry and 152 * returns the number of detected names as a function result. 153 */ 154 static int feature_flags(char * const names[], int num, u8 *flags) 155 { 156 int i, j; 157 158 *flags = 0; 159 for (i = 0; i < num; i++) { 160 for (j = 0; j < 8; j++) { 161 if (strcmp(pwrman_feat[j], names[i]) == 0) { 162 *flags |= 1 << j; 163 break; 164 } 165 } 166 if (j > 7) 167 break; 168 } 169 return i; 170 } 171 172 void ethernut5_print_power(void) 173 { 174 u8 flags; 175 int i; 176 177 flags = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA); 178 for (i = 0; i < 2; i++) { 179 if (flags) { 180 print_flagged_features(flags); 181 printf("%s\n", i ? "off" : "on"); 182 } 183 flags = ~flags; 184 } 185 } 186 187 void ethernut5_print_celsius(void) 188 { 189 int val; 190 191 /* Read ADC value from LM50 and return Celsius degrees. */ 192 val = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_TEMP); 193 val *= 5000; /* 100mV/degree with 5V reference */ 194 val += 128; /* 8 bit resolution */ 195 val /= 256; 196 val -= 450; /* Celsius offset, still x10 */ 197 /* Output full degrees. */ 198 printf("%d\n", (val + 5) / 10); 199 } 200 201 void ethernut5_print_voltage(void) 202 { 203 int val; 204 205 /* Read ADC value from divider and return voltage. */ 206 val = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_VAUX); 207 /* Resistors are 100k and 12.1k */ 208 val += 5; 209 val *= 180948; 210 val /= 100000; 211 val++; 212 /* Calculation was done in 0.1V units. */ 213 printf("%d\n", (val + 5) / 10); 214 } 215 216 /* 217 * Process the board specific 'pwrman' command. 218 */ 219 int do_pwrman(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 220 { 221 u8 val; 222 int i; 223 224 if (argc == 1) { 225 ethernut5_print_power(); 226 } else if (argc == 2 && strcmp(argv[1], "reset") == 0) { 227 at91_set_pio_output(AT91_PIO_PORTB, 8, 1); 228 udelay(100); 229 at91_set_pio_output(AT91_PIO_PORTB, 8, 0); 230 udelay(100000); 231 } else if (argc == 2 && strcmp(argv[1], "temp") == 0) { 232 ethernut5_print_celsius(); 233 } else if (argc == 2 && strcmp(argv[1], "vaux") == 0) { 234 ethernut5_print_voltage(); 235 } else if (argc == 2 && strcmp(argv[1], "version") == 0) { 236 ethernut5_print_version(); 237 } else if (strcmp(argv[1], "led") == 0) { 238 /* Control the green status LED. Blink frequency unit 239 ** is 0.1s, very roughly. */ 240 if (argc == 2) { 241 /* No more arguments, output current settings. */ 242 val = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_LEDCTL); 243 printf("led %u %u\n", val >> 4, val & 15); 244 } else { 245 /* First argument specifies the on-time. */ 246 val = (u8) simple_strtoul(argv[2], NULL, 0); 247 val <<= 4; 248 if (argc > 3) { 249 /* Second argument specifies the off-time. */ 250 val |= (u8) (simple_strtoul(argv[3], NULL, 0) 251 & 15); 252 } 253 /* Update the LED control register. */ 254 i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_LEDCTL, val); 255 } 256 } else { 257 /* We expect a list of features followed an optional status. */ 258 argc--; 259 i = feature_flags(&argv[1], argc, &val); 260 if (argc == i) { 261 /* We got a list only, print status. */ 262 val &= i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_STA); 263 if (val) { 264 if (i > 1) 265 print_flagged_features(val); 266 printf("active\n"); 267 } else { 268 printf("inactive\n"); 269 } 270 } else { 271 /* More arguments. */ 272 if (i == 0) { 273 /* No given feature, use despensibles. */ 274 val = PWRMAN_DISPENSIBLE; 275 } 276 if (strcmp(argv[i + 1], "on") == 0) { 277 /* Enable features. */ 278 i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA, 279 val); 280 } else if (strcmp(argv[i + 1], "off") == 0) { 281 /* Disable features. */ 282 i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS, 283 val); 284 } else { 285 printf("Bad parameter %s\n", argv[i + 1]); 286 return 1; 287 } 288 } 289 } 290 return 0; 291 } 292 293 U_BOOT_CMD( 294 pwrman, CONFIG_SYS_MAXARGS, 1, do_pwrman, 295 "power management", 296 "- print settings\n" 297 "pwrman feature ...\n" 298 " - print status\n" 299 "pwrman [feature ...] on|off\n" 300 " - enable/disable specified or all dispensible features\n" 301 "pwrman led [on-time [off-time]]\n" 302 " - print or set led blink timer\n" 303 "pwrman temp\n" 304 " - print board temperature (Celsius)\n" 305 "pwrman vaux\n" 306 " - print auxiliary input voltage\n" 307 "pwrman reset\n" 308 " - reset power management controller\n" 309 "pwrman version\n" 310 " - print firmware version\n" 311 "\n" 312 " features, (*)=dispensible:\n" 313 " board - 1.8V and 3.3V supply\n" 314 " vbin - supply via USB device connector\n" 315 " vbout - USB host connector supply(*)\n" 316 " mmc - MMC slot supply(*)\n" 317 " rs232 - RS232 driver\n" 318 " ethclk - Ethernet PHY clock(*)\n" 319 " ethrst - Ethernet PHY reset\n" 320 " wakeup - RTC alarm" 321 ); 322 #endif /* CONFIG_CMD_BSP */ 323