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