aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm-2.4/files/arch/mips/bcm947xx/hndpmu.c
blob: 0e95f2cfe953869879190ae920292b8f0e4f45f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * NVRAM variable manipulation
 *
 * Copyright 2004, Broadcom Corporation
 * All Rights Reserved.
 * 
 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
 *
 * $Id$
 */

#ifndef _bcmnvram_h_
#define _bcmnvram_h_

#ifndef _LANGUAGE_ASSEMBLY

#include <typedefs.h>

struct nvram_header {
	uint32 magic;
	uint32 len;
	uint32 crc_ver_init;	/* 0:7 crc, 8:15 ver, 16:27 init, mem. test 28, 29-31 reserved */
	uint32 config_refresh;	/* 0:15 config, 16:31 refresh */
	uint32 config_ncdl;	/* ncdl values for memc */
};

struct nvram_tuple {
	char *name;
	char *value;
	struct nvram_tuple *next;
};

/*
 * Initialize NVRAM access. May be unnecessary or undefined on certain
 * platforms.
 */
extern int nvram_init(void *sbh);

/*
 * Disable NVRAM access. May be unnecessary or undefined on certain
 * platforms.
 */
extern void nvram_exit(void);

/*
 * Get the value of an NVRAM variable. The pointer returned may be
 * invalid after a set.
 * @param	name	name of variable to get
 * @return	value of variable or NULL if undefined
 */
extern char * nvram_get(const char *name);

/* 
 * Get the value of an NVRAM variable.
 * @param	name	name of variable to get
 * @return	value of variable or NUL if undefined
 */
#define nvram_safe_get(name) (nvram_get(name) ? : "")

#define nvram_safe_unset(name) ({ \
	if(nvram_get(name)) \
		nvram_unset(name); \
})

#define nvram_safe_set(name, value) ({ \
	if(!nvram_get(name) || strcmp(nvram_get(name), value)) \
		nvram_set(name, value); \
})

/*
 * Match an NVRAM variable.
 * @param	name	name of variable to match
 * @param	match	value to compare against value of variable
 * @return	TRUE if variable is defined and its value is string equal
 *		to match or FALSE otherwise
 */
static INLINE int
nvram_match(char *name, char *match) {
	const char *value = nvram_get(name);
	return (value && !strcmp(value, match));
}

/*
 * Inversely match an NVRAM variable.
 * @param	name	name of variable to match
 * @param	match	value to compare against value of variable
 * @return	TRUE if variable is defined and its value is not string
 *		equal to invmatch or FALSE otherwise
 */
static INLINE int
nvram_invmatch(char *name, char *invmatch) {
	const char *value = nvram_get(name);
	return (value && strcmp(value, invmatch));
}

/*
 * Set the value of an NVRAM variable. The name and value strings are
 * copied into private storage. Pointers to previously set values
 * may become invalid. The new value may be immediately
 * retrieved but will not be permanently stored until a commit.
 * @param	name	name of variable to set
 * @param	value	value of variable
 * @return	0 on success and errno on failure
 */
extern int nvram_set(const char *name, const char *value);

/*
 * Unset an NVRAM variable. Pointers to previously set values
 * remain valid until a set.
 * @param	name	name of variable to unset
 * @return	0 on success and errno on failure
 * NOTE: use nvram_commit to commit this change to flash.
 */
extern int nvram_unset(const char *name);

/*
 * Commit NVRAM variables to permanent storage. All pointers to values
 * may be invalid after a commit.
 * NVRAM values are undefined after a commit.
 * @return	0 on success and errno on failure
 */
extern int nvram_commit(void);

/*
 * Get all NVRAM variables (format name=value\0 ... \0\0).
 * @param	buf	buffer to store variables
 * @param	count	size of buffer in bytes
 * @return	0 on success and errno on failure
 */
extern int nvram_getall(char *buf, int count);

#endif /* _LANGUAGE_ASSEMBLY */

#define NVRAM_MAGIC		0x48534C46	/* 'FLSH' */
#define NVRAM_VERSION		1
#define NVRAM_HEADER_SIZE	20
#define NVRAM_SPACE		0x8000
#define FLASH_BASE		0xbfc00000	/* Extif core */
#define FLASH_MIN		0x00100000	/* Minimum flash size */
#define FLASH_MAX		0x00400000	/* Maximum flash size with extif */

#endif /* _bcmnvram_h_ */
' href='#n606'>606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
/*
 * Misc utility routines for accessing PMU corerev specific features
 * of the SiliconBackplane-based Broadcom chips.
 *
 * Copyright 2007, Broadcom Corporation
 * All Rights Reserved.
 * 
 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
 */

#include <typedefs.h>
#include <bcmdefs.h>
#include <osl.h>
#include <sbutils.h>
#include <bcmdevs.h>
#include <sbconfig.h>
#include <sbchipc.h>
#include <hndpmu.h>

/* debug/trace */
#define	PMU_ERROR(args)

#ifdef BCMDBG
#define	PMU_MSG(args)	printf args
#else
#define	PMU_MSG(args)
#endif /* BCMDBG */

/* PMU & control */
/* PMU rev 0 pll control for BCM4328 and BCM5354 */
static void sb_pmu0_pllinit0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc,
			      uint32 xtal);
static uint32 sb_pmu0_alpclk0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc);
static uint32 sb_pmu0_cpuclk0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc);
/* PMU rev 0 pll control for BCM4325 BCM4329 */
static void sb_pmu1_pllinit0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc,
			      uint32 xtal);
static uint32 sb_pmu1_cpuclk0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc);
static uint32 sb_pmu1_alpclk0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc);

/* Setup switcher voltage */
void
BCMINITFN (sb_pmu_set_switcher_voltage) (sb_t * sbh, osl_t * osh,
					 uint8 bb_voltage, uint8 rf_voltage)
{
  chipcregs_t *cc;
  uint origidx;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  /* Remember original core before switch to chipc */
  origidx = sb_coreidx (sbh);
  cc = sb_setcore (sbh, SB_CC, 0);
  ASSERT (cc);

  W_REG (osh, &cc->regcontrol_addr, 0x01);
  W_REG (osh, &cc->regcontrol_data, (uint32) (bb_voltage & 0x1f) << 22);

  W_REG (osh, &cc->regcontrol_addr, 0x00);
  W_REG (osh, &cc->regcontrol_data, (uint32) (rf_voltage & 0x1f) << 14);

  /* Return to original core */
  sb_setcoreidx (sbh, origidx);
}

void
sb_pmu_set_ldo_voltage (sb_t * sbh, osl_t * osh, uint8 ldo, uint8 voltage)
{
  uint8 sr_cntl_shift, rc_shift, shift, mask;
  uint32 addr;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  switch (sbh->chip)
    {
    case BCM4328_CHIP_ID:
    case BCM5354_CHIP_ID:
      switch (ldo)
	{
	case SET_LDO_VOLTAGE_LDO1:
	  addr = 2;
	  sr_cntl_shift = 8;
	  rc_shift = 17;
	  mask = 0xf;
	  break;
	case SET_LDO_VOLTAGE_LDO2:
	  addr = 3;
	  sr_cntl_shift = 0;
	  rc_shift = 1;
	  mask = 0xf;
	  break;
	case SET_LDO_VOLTAGE_LDO3:
	  addr = 3;
	  sr_cntl_shift = 0;
	  rc_shift = 9;
	  mask = 0xf;
	  break;
	case SET_LDO_VOLTAGE_PAREF:
	  addr = 3;
	  sr_cntl_shift = 0;
	  rc_shift = 17;
	  mask = 0x3f;
	  break;
	default:
	  ASSERT (FALSE);
	  return;
	}
      break;
    case BCM4312_CHIP_ID:
      switch (ldo)
	{
	case SET_LDO_VOLTAGE_PAREF:
	  addr = 0;
	  sr_cntl_shift = 0;
	  rc_shift = 21;
	  mask = 0x3f;
	  break;
	default:
	  ASSERT (FALSE);
	  return;
	}
      break;
    default:
      ASSERT (FALSE);
      return;
    }

  shift = sr_cntl_shift + rc_shift;

  sb_corereg (sbh, SB_CC_IDX, OFFSETOF (chipcregs_t, regcontrol_addr),
	      ~0, addr);
  sb_corereg (sbh, SB_CC_IDX, OFFSETOF (chipcregs_t, regcontrol_data),
	      mask << shift, (voltage & mask) << shift);
}

void
sb_pmu_paref_ldo_enable (sb_t * sbh, osl_t * osh, bool enable)
{
  uint ldo = 0;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  switch (sbh->chip)
    {
    case BCM4328_CHIP_ID:
      ldo = RES4328_PA_REF_LDO;
      break;
    case BCM5354_CHIP_ID:
      ldo = RES5354_PA_REF_LDO;
      break;
    case BCM4312_CHIP_ID:
      ldo = RES4312_PA_REF_LDO;
      break;
    default:
      return;
    }

  sb_corereg (sbh, SB_CC_IDX, OFFSETOF (chipcregs_t, min_res_mask),
	      PMURES_BIT (ldo), enable ? PMURES_BIT (ldo) : 0);
}

uint16 BCMINITFN (sb_pmu_fast_pwrup_delay) (sb_t * sbh, osl_t * osh)
{
  uint16 delay = PMU_MAX_TRANSITION_DLY;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  switch (sbh->chip)
    {
    case BCM4328_CHIP_ID:
      delay = 7000;
      break;

    case BCM4325_CHIP_ID:
    case BCM4312_CHIP_ID:
#ifdef BCMQT
      delay = 70;
#else
      delay = 2800;
#endif
      break;

    default:
      PMU_MSG (("No PMU fast power up delay specified "
		"for chip %x rev %d, using default %d us\n",
		sbh->chip, sbh->chiprev, delay));
      break;
    }

  return delay;
}

uint32 BCMINITFN (sb_pmu_force_ilp) (sb_t * sbh, osl_t * osh, bool force)
{
  chipcregs_t *cc;
  uint origidx;
  uint32 oldpmucontrol;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  /* Remember original core before switch to chipc */
  origidx = sb_coreidx (sbh);
  cc = sb_setcore (sbh, SB_CC, 0);
  ASSERT (cc);

  oldpmucontrol = R_REG (osh, &cc->pmucontrol);
  if (force)
    W_REG (osh, &cc->pmucontrol, oldpmucontrol &
	   ~(PCTL_HT_REQ_EN | PCTL_ALP_REQ_EN));
  else
    W_REG (osh, &cc->pmucontrol, oldpmucontrol |
	   (PCTL_HT_REQ_EN | PCTL_ALP_REQ_EN));

  /* Return to original core */
  sb_setcoreidx (sbh, origidx);

  return oldpmucontrol;
}

/* Setup min/max resources and up/down timers */
typedef struct
{
  uint8 resnum;
  uint16 updown;
} pmu_res_updown_t;

typedef struct
{
  uint8 resnum;
  int8 action;			/* 0 - set, 1 - add, -1 - remove */
  uint32 depend_mask;
} pmu_res_depend_t;

static const pmu_res_updown_t
BCMINITDATA (bcm4328a0_res_updown)[] =
{
  {
  RES4328_EXT_SWITCHER_PWM, 0x0101},
  {
  RES4328_BB_SWITCHER_PWM, 0x1f01},
  {
  RES4328_BB_SWITCHER_BURST, 0x010f},
  {
  RES4328_BB_EXT_SWITCHER_BURST, 0x0101},
  {
  RES4328_ILP_REQUEST, 0x0202},
  {
  RES4328_RADIO_SWITCHER_PWM, 0x0f01},
  {
  RES4328_RADIO_SWITCHER_BURST, 0x0f01},
  {
  RES4328_ROM_SWITCH, 0x0101},
  {
  RES4328_PA_REF_LDO, 0x0f01},
  {
  RES4328_RADIO_LDO, 0x0f01},
  {
  RES4328_AFE_LDO, 0x0f01},
  {
  RES4328_PLL_LDO, 0x0f01},
  {
  RES4328_BG_FILTBYP, 0x0101},
  {
  RES4328_TX_FILTBYP, 0x0101},
  {
  RES4328_RX_FILTBYP, 0x0101},
  {
  RES4328_XTAL_PU, 0x0101},
  {
  RES4328_XTAL_EN, 0xa001},
  {
  RES4328_BB_PLL_FILTBYP, 0x0101},
  {
  RES4328_RF_PLL_FILTBYP, 0x0101},
  {
  RES4328_BB_PLL_PU, 0x0701}
};

static const pmu_res_depend_t
BCMINITDATA (bcm4328a0_res_depend)[] =
{
  /* Adjust ILP request resource not to force ext/BB switchers into burst mode */
  {
  RES4328_ILP_REQUEST, 0,
      PMURES_BIT (RES4328_EXT_SWITCHER_PWM) |
      PMURES_BIT (RES4328_BB_SWITCHER_PWM)}
};

#ifdef BCMQT			/* for power save on slow QT/small beacon interval */
static const pmu_res_updown_t
BCMINITDATA (bcm4325a0_res_updown_qt)[] =
{
  {
  RES4325_HT_AVAIL, 0x0300},
  {
  RES4325_BBPLL_PWRSW_PU, 0x0101},
  {
  RES4325_RFPLL_PWRSW_PU, 0x0101},
  {
  RES4325_ALP_AVAIL, 0x0100},
  {
  RES4325_XTAL_PU, 0x1000},
  {
  RES4325_LNLDO1_PU, 0x0800},
  {
  RES4325_CLDO_CBUCK_PWM, 0x0101},
  {
  RES4325_CBUCK_PWM, 0x0803}
};
#else
static const pmu_res_updown_t
BCMINITDATA (bcm4325a0_res_updown)[] =
{
  {
  RES4325_XTAL_PU, 0x1501}
};
#endif /* !BCMQT */

static const pmu_res_depend_t
BCMINITDATA (bcm4325a0_res_depend)[] =
{
  /* Adjust HT Avail resource dependencies */
  {
  RES4325_HT_AVAIL, 1,
      PMURES_BIT (RES4325_RX_PWRSW_PU) | PMURES_BIT (RES4325_TX_PWRSW_PU) |
      PMURES_BIT (RES4325_LOGEN_PWRSW_PU) | PMURES_BIT (RES4325_AFE_PWRSW_PU)}
};

void BCMINITFN (sb_pmu_res_init) (sb_t * sbh, osl_t * osh)
{
  chipcregs_t *cc;
  uint origidx;
  const pmu_res_updown_t *pmu_res_updown_table = NULL;
  int pmu_res_updown_table_sz = 0;
  const pmu_res_depend_t *pmu_res_depend_table = NULL;
  int pmu_res_depend_table_sz = 0;
  uint32 min_mask = 0, max_mask = 0;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  /* Remember original core before switch to chipc */
  origidx = sb_coreidx (sbh);
  cc = sb_setcore (sbh, SB_CC, 0);
  ASSERT (cc);

  switch (sbh->chip)
    {
    case BCM4328_CHIP_ID:
      /* Down to ILP request excluding ROM */
      min_mask = PMURES_BIT (RES4328_EXT_SWITCHER_PWM) |
	PMURES_BIT (RES4328_BB_SWITCHER_PWM) | PMURES_BIT (RES4328_XTAL_EN);
#ifdef BCMROMOFFLOAD
      /* Including ROM */
      min_mask |= PMURES_BIT (RES4328_ROM_SWITCH);
#endif
      /* Allow (but don't require) PLL to turn on */
      max_mask = 0xfffff;
      pmu_res_updown_table = bcm4328a0_res_updown;
      pmu_res_updown_table_sz = ARRAYSIZE (bcm4328a0_res_updown);
      pmu_res_depend_table = bcm4328a0_res_depend;
      pmu_res_depend_table_sz = ARRAYSIZE (bcm4328a0_res_depend);
      break;
    case BCM4312_CHIP_ID:
      /* keep default
       * min_mask = 0xcbb; max_mask = 0x7ffff;
       * pmu_res_updown_table_sz = 0;
       * pmu_res_depend_table_sz = 0;
       */
      break;
    case BCM5354_CHIP_ID:
      /* Allow (but don't require) PLL to turn on */
      max_mask = 0xfffff;
      break;

    case BCM4325_CHIP_ID:
      /* Leave OTP powered up and power it down later. */
      min_mask =
	PMURES_BIT (RES4325_CBUCK_BURST) | PMURES_BIT (RES4325_LNLDO2_PU);
      if (((sbh->chipst & CST4325_PMUTOP_2B_MASK) >>
	   CST4325_PMUTOP_2B_SHIFT) == 1)
	min_mask |= PMURES_BIT (RES4325_CLDO_CBUCK_BURST);
      /* Allow (but don't require) PLL to turn on */
      max_mask = 0x3fffff;
#ifdef BCMQT
      pmu_res_updown_table = bcm4325a0_res_updown_qt;
      pmu_res_updown_table_sz = ARRAYSIZE (bcm4325a0_res_updown_qt);
#else
      pmu_res_updown_table = bcm4325a0_res_updown;
      pmu_res_updown_table_sz = ARRAYSIZE (bcm4325a0_res_updown);
      pmu_res_depend_table = bcm4325a0_res_depend;
      pmu_res_depend_table_sz = ARRAYSIZE (bcm4325a0_res_depend);
#endif
      break;

    default:
      break;
    }

  /* Program up/down timers */
  while (pmu_res_updown_table_sz--)
    {
      ASSERT (pmu_res_updown_table);
      W_REG (osh, &cc->res_table_sel,
	     pmu_res_updown_table[pmu_res_updown_table_sz].resnum);
      W_REG (osh, &cc->res_updn_timer,
	     pmu_res_updown_table[pmu_res_updown_table_sz].updown);
    }

  /* Program resource dependencies table */
  while (pmu_res_depend_table_sz--)
    {
      ASSERT (pmu_res_depend_table);
      W_REG (osh, &cc->res_table_sel,
	     pmu_res_depend_table[pmu_res_depend_table_sz].resnum);
      switch (pmu_res_depend_table[pmu_res_depend_table_sz].action)
	{
	case 0:
	  W_REG (osh, &cc->res_dep_mask,
		 pmu_res_depend_table[pmu_res_depend_table_sz].depend_mask);
	  break;
	case 1:
	  OR_REG (osh, &cc->res_dep_mask,
		  pmu_res_depend_table[pmu_res_depend_table_sz].depend_mask);
	  break;
	case -1:
	  AND_REG (osh, &cc->res_dep_mask,
		   ~pmu_res_depend_table[pmu_res_depend_table_sz].
		   depend_mask);
	  break;
	default:
	  ASSERT (0);
	  break;
	}
    }

  /* program min resource mask */
  if (min_mask)
    {
      PMU_MSG (("Changing min_res_mask to 0x%x\n", min_mask));
      W_REG (osh, &cc->min_res_mask, min_mask);
    }
  /* program max resource mask */
  if (max_mask)
    {
      PMU_MSG (("Changing max_res_mask to 0x%x\n", max_mask));
      W_REG (osh, &cc->max_res_mask, max_mask);
    }

  /* Return to original core */
  sb_setcoreidx (sbh, origidx);
}

/* setup pll and query clock speed */
typedef struct
{
  uint16 freq;
  uint8 xf;
  uint8 wbint;
  uint32 wbfrac;
} pmu0_xtaltab0_t;

/* the following table is based on 880Mhz Fvco */
#define PMU0_PLL0_FVCO	880000	/* Fvco 880Mhz */
static const pmu0_xtaltab0_t
BCMINITDATA (pmu0_xtaltab0)[] =
{
  {
  12000, 1, 73, 349525},
  {
  13000, 2, 67, 725937},
  {
  14400, 3, 61, 116508},
  {
  15360, 4, 57, 305834},
  {
  16200, 5, 54, 336579},
  {
  16800, 6, 52, 399457},
  {
  19200, 7, 45, 873813},
  {
  19800, 8, 44, 466033},
  {
  20000, 9, 44, 0},
  {
  25000, 10, 70, 419430},
  {
  26000, 11, 67, 725937},
  {
  30000, 12, 58, 699050},
  {
  38400, 13, 45, 873813},
  {
  40000, 14, 45, 0},
  {
  0, 0, 0, 0}
};

#ifdef BCMUSBDEV
#define	PMU0_XTAL0_DEFAULT	11
#else
#define PMU0_XTAL0_DEFAULT	8
#endif

#ifdef BCMUSBDEV
/*
 * Set new backplane PLL clock frequency
 */
static void BCMINITFN (sb_pmu0_sbclk4328) (sb_t * sbh, int freq)
{
  uint32 tmp, oldmax, oldmin, origidx;
  chipcregs_t *cc;

  /* Remember original core before switch to chipc */
  origidx = sb_coreidx (sbh);
  cc = sb_setcore (sbh, SB_CC, 0);
  ASSERT (cc);

  /* Set new backplane PLL clock */
  W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL0);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  tmp &= ~(PMU0_PLL0_PC0_DIV_ARM_MASK);
  tmp |= freq << PMU0_PLL0_PC0_DIV_ARM_SHIFT;
  W_REG (osh, &cc->pllcontrol_data, tmp);

  /* Power cycle BB_PLL_PU by disabling/enabling it to take on new freq */
  /* Disable PLL */
  oldmin = R_REG (osh, &cc->min_res_mask);
  oldmax = R_REG (osh, &cc->max_res_mask);
  W_REG (osh, &cc->min_res_mask, oldmin & ~PMURES_BIT (RES4328_BB_PLL_PU));
  W_REG (osh, &cc->max_res_mask, oldmax & ~PMURES_BIT (RES4328_BB_PLL_PU));

  /* It takes over several hundred usec to re-enable the PLL since the
   * sequencer state machines run on ILP clock. Set delay at 450us to be safe.
   *
   * Be sure PLL is powered down first before re-enabling it.
   */

  OSL_DELAY (PLL_DELAY);
  SPINWAIT ((R_REG (osh, &cc->res_state) & PMURES_BIT (RES4328_BB_PLL_PU)),
	    PLL_DELAY * 3);

  if (R_REG (osh, &cc->res_state) & PMURES_BIT (RES4328_BB_PLL_PU))
    {
      /* If BB_PLL not powered down yet, new backplane PLL clock
       *  may not take effect.
       *
       * Still early during bootup so no serial output here.
       */
      PMU_ERROR (("Fatal: BB_PLL not power down yet!\n"));
      ASSERT (!
	      (R_REG (osh, &cc->res_state) & PMURES_BIT (RES4328_BB_PLL_PU)));
    }

  /* Enable PLL */
  W_REG (osh, &cc->max_res_mask, oldmax);

  /* Return to original core */
  sb_setcoreidx (sbh, origidx);
}
#endif /* BCMUSBDEV */

/* Set up PLL registers in the PMU as per the crystal speed.
 * Uses xtalfreq variable, or passed-in default.
 */
static void
BCMINITFN (sb_pmu0_pllinit0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc,
			      uint32 xtal)
{
  uint32 tmp;
  const pmu0_xtaltab0_t *xt;

  if ((sb_chip (sbh) == BCM5354_CHIP_ID) && (xtal == 0))
    {
      /* 5354 has xtal freq of 25MHz */
      xtal = 25000;
    }

  /* Find the frequency in the table */
  for (xt = pmu0_xtaltab0; xt->freq; xt++)
    if (xt->freq == xtal)
      break;
  if (xt->freq == 0)
    xt = &pmu0_xtaltab0[PMU0_XTAL0_DEFAULT];

  PMU_MSG (("XTAL %d (%d)\n", xtal, xt->xf));

  /* Check current PLL state */
  tmp = (R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >>
    PCTL_XTALFREQ_SHIFT;
  if (tmp == xt->xf)
    {
      PMU_MSG (("PLL already programmed for %d.%d MHz\n",
		(xt->freq / 1000), (xt->freq % 1000)));

#ifdef BCMUSBDEV
      if (sbh->chip == BCM4328_CHIP_ID)
	sb_pmu0_sbclk4328 (sbh, PMU0_PLL0_PC0_DIV_ARM_88MHZ);
#endif
      return;
    }

  if (tmp)
    {
      PMU_MSG (("Reprogramming PLL for %d.%d MHz (was %d.%dMHz)\n",
		(xt->freq / 1000), (xt->freq % 1000),
		(pmu0_xtaltab0[tmp - 1].freq / 1000),
		(pmu0_xtaltab0[tmp - 1].freq % 1000)));
    }
  else
    {
      PMU_MSG (("Programming PLL for %d.%d MHz\n", (xt->freq / 1000),
		(xt->freq % 1000)));
    }

  /* Make sure the PLL is off */
  switch (sbh->chip)
    {
    case BCM4328_CHIP_ID:
      AND_REG (osh, &cc->min_res_mask, ~PMURES_BIT (RES4328_BB_PLL_PU));
      AND_REG (osh, &cc->max_res_mask, ~PMURES_BIT (RES4328_BB_PLL_PU));
      break;
    case BCM5354_CHIP_ID:
      AND_REG (osh, &cc->min_res_mask, ~PMURES_BIT (RES5354_BB_PLL_PU));
      AND_REG (osh, &cc->max_res_mask, ~PMURES_BIT (RES5354_BB_PLL_PU));
      break;
    default:
      ASSERT (0);
    }
  SPINWAIT (R_REG (osh, &cc->clk_ctl_st) & CCS0_HTAVAIL,
	    PMU_MAX_TRANSITION_DLY);
  ASSERT (!(R_REG (osh, &cc->clk_ctl_st) & CCS0_HTAVAIL));

  PMU_MSG (("Done masking\n"));

  /* Write PDIV in pllcontrol[0] */
  W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL0);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  if (xt->freq >= PMU0_PLL0_PC0_PDIV_FREQ)
    tmp |= PMU0_PLL0_PC0_PDIV_MASK;
  else
    tmp &= ~PMU0_PLL0_PC0_PDIV_MASK;
  W_REG (osh, &cc->pllcontrol_data, tmp);

  /* Write WILD in pllcontrol[1] */
  W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL1);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  tmp =
    ((tmp & ~(PMU0_PLL0_PC1_WILD_INT_MASK | PMU0_PLL0_PC1_WILD_FRAC_MASK)) |
     (((xt->
	wbint << PMU0_PLL0_PC1_WILD_INT_SHIFT) & PMU0_PLL0_PC1_WILD_INT_MASK)
      | ((xt->wbfrac << PMU0_PLL0_PC1_WILD_FRAC_SHIFT) &
	 PMU0_PLL0_PC1_WILD_FRAC_MASK)));
  if (xt->wbfrac == 0)
    tmp |= PMU0_PLL0_PC1_STOP_MOD;
  else
    tmp &= ~PMU0_PLL0_PC1_STOP_MOD;
  W_REG (osh, &cc->pllcontrol_data, tmp);

  /* Write WILD in pllcontrol[2] */
  W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL2);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  tmp = ((tmp & ~PMU0_PLL0_PC2_WILD_INT_MASK) |
	 ((xt->wbint >> PMU0_PLL0_PC2_WILD_INT_SHIFT) &
	  PMU0_PLL0_PC2_WILD_INT_MASK));
  W_REG (osh, &cc->pllcontrol_data, tmp);

  PMU_MSG (("Done pll\n"));

  /* Write XtalFreq. Set the divisor also. */
  tmp = R_REG (osh, &cc->pmucontrol);
  tmp = ((tmp & ~PCTL_ILP_DIV_MASK) |
	 (((((xt->freq + 127) / 128) - 1) << PCTL_ILP_DIV_SHIFT) &
	  PCTL_ILP_DIV_MASK));
  tmp = ((tmp & ~PCTL_XTALFREQ_MASK) |
	 ((xt->xf << PCTL_XTALFREQ_SHIFT) & PCTL_XTALFREQ_MASK));
  W_REG (osh, &cc->pmucontrol, tmp);
}

static uint32
BCMINITFN (sb_pmu0_alpclk0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc)
{
  const pmu0_xtaltab0_t *xt;
  uint32 xf;

  /* Find the frequency in the table */
  xf = (R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >>
    PCTL_XTALFREQ_SHIFT;
  for (xt = pmu0_xtaltab0; xt->freq; xt++)
    if (xt->xf == xf)
      break;
  if (xt->freq == 0)
    xt = &pmu0_xtaltab0[PMU0_XTAL0_DEFAULT];

  return xt->freq * 1000;
}

static uint32
BCMINITFN (sb_pmu0_cpuclk0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc)
{
  const pmu0_xtaltab0_t *xt;
  uint32 xf, tmp, divarm;
#ifdef BCMDBG
  uint32 pdiv, wbint, wbfrac, fvco;
#endif

  if (sb_chip (sbh) == BCM5354_CHIP_ID)
    {
      /* 5354 gets sb clock of 120MHz from main pll */
      return 120000000;
    }

  /* Find the xtal frequency in the table */
  xf = (R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >>
    PCTL_XTALFREQ_SHIFT;
  for (xt = pmu0_xtaltab0; xt->freq; xt++)
    if (xt->xf == xf)
      break;
  if (xt->freq == 0)
    xt = &pmu0_xtaltab0[PMU0_XTAL0_DEFAULT];

  /* Read divarm from pllcontrol[0] */
  W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL0);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  divarm = (tmp & PMU0_PLL0_PC0_DIV_ARM_MASK) >> PMU0_PLL0_PC0_DIV_ARM_SHIFT;

#ifdef BCMDBG
  /* Calculate Fvco based on xtal freq, pdiv, and wild */
  pdiv = tmp & PMU0_PLL0_PC0_PDIV_MASK;

  W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL1);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  wbfrac =
    (tmp & PMU0_PLL0_PC1_WILD_FRAC_MASK) >> PMU0_PLL0_PC1_WILD_FRAC_SHIFT;
  wbint = (tmp & PMU0_PLL0_PC1_WILD_INT_MASK) >> PMU0_PLL0_PC1_WILD_INT_SHIFT;

  W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL2);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  wbint +=
    (tmp & PMU0_PLL0_PC2_WILD_INT_MASK) << PMU0_PLL0_PC2_WILD_INT_SHIFT;

  fvco = (xt->freq * wbint) << 8;
  fvco += (xt->freq * (wbfrac >> 10)) >> 2;
  fvco += (xt->freq * (wbfrac & 0x3ff)) >> 10;
  fvco >>= 8;
  fvco >>= pdiv;
  fvco /= 1000;
  fvco *= 1000;

  PMU_MSG (("sb_pmu0_cpuclk0: wbint %u wbfrac %u fvco %u\n",
	    wbint, wbfrac, fvco));
  ASSERT (fvco == PMU0_PLL0_FVCO);
#endif /* BCMDBG */

  /* Return ARM/SB clock */
  return PMU0_PLL0_FVCO / (divarm + PMU0_PLL0_PC0_DIV_ARM_BASE) * 1000;
}

/* PMU corerev 1 pll programming for BCM4325 */
/* setup pll and query clock speed */
typedef struct
{
  uint16 fref;
  uint8 xf;
  uint8 p1div;
  uint8 p2div;
  uint8 ndiv_int;
  uint32 ndiv_frac;
} pmu1_xtaltab0_t;

/* the following table is based on 880Mhz Fvco */
#define PMU1_PLL0_FVCO	880000	/* Fvco 880Mhz */
static const pmu1_xtaltab0_t
BCMINITDATA (pmu1_xtaltab0)[] =
{
  {
  12000, 1, 3, 22, 0x9, 0xFFFFEF},
  {
  13000, 2, 1, 6, 0xb, 0x483483},
  {
  14400, 3, 1, 10, 0xa, 0x1C71C7},
  {
  15360, 4, 1, 5, 0xb, 0x755555},
  {
  16200, 5, 1, 10, 0x5, 0x6E9E06},
  {
  16800, 6, 1, 10, 0x5, 0x3Cf3Cf},
  {
  19200, 7, 1, 9, 0x5, 0x17B425},
  {
  19800, 8, 1, 11, 0x4, 0xA57EB},
  {
  20000, 9, 1, 11, 0x4, 0x0},
  {
  24000, 10, 3, 11, 0xa, 0x0},
  {
  25000, 11, 5, 16, 0xb, 0x0},
  {
  26000, 12, 1, 2, 0x10, 0xEC4EC4},
  {
  30000, 13, 3, 8, 0xb, 0x0},
  {
  38400, 14, 1, 5, 0x4, 0x955555},
  {
  40000, 15, 1, 2, 0xb, 0},
  {
  0, 0, 0, 0, 0, 0}
};

/* Default to 15360Khz crystal */
#define PMU1_XTAL0_DEFAULT	3

static uint32
BCMINITFN (sb_pmu1_alpclk0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc)
{
  const pmu1_xtaltab0_t *xt;
  uint32 xf;

  /* Find the frequency in the table */
  xf = (R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >>
    PCTL_XTALFREQ_SHIFT;
  for (xt = pmu1_xtaltab0; xt->fref; xt++)
    if (xt->xf == xf)
      break;
  if (xt->fref == 0)
    xt = &pmu1_xtaltab0[PMU1_XTAL0_DEFAULT];

  return xt->fref * 1000;
}

/* Set up PLL registers in the PMU as per the crystal speed.
 * Uses xtalfreq variable, or passed-in default.
 */
static void
BCMINITFN (sb_pmu1_pllinit0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc,
			      uint32 xtal)
{
  const pmu1_xtaltab0_t *xt;
  uint32 tmp;
  uint32 buf_strength = 0;

  /* 4312: assume default works */
  if (sbh->chip == BCM4312_CHIP_ID)
    return;

  /* Find the frequency in the table */
  for (xt = pmu1_xtaltab0; xt->fref; xt++)
    if (xt->fref == xtal)
      break;
  if (xt->fref == 0)
    xt = &pmu1_xtaltab0[PMU1_XTAL0_DEFAULT];

  PMU_MSG (("XTAL %d (%d)\n", xtal, xt->xf));

  /* Check current PLL state */
  if (((R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >>
       PCTL_XTALFREQ_SHIFT) == xt->xf)
    {
      PMU_MSG (("PLL already programmed for %d.%d MHz\n",
		(xt->fref / 1000), (xt->fref % 1000)));
      return;
    }

  PMU_MSG (("Programming PLL for %d.%d MHz\n", (xt->fref / 1000),
	    (xt->fref % 1000)));

  /* Make sure the PLL is off */
  switch (sbh->chip)
    {
    case BCM4325_CHIP_ID:
      AND_REG (osh, &cc->min_res_mask,
	       ~(PMURES_BIT (RES4325_BBPLL_PWRSW_PU) |
		 PMURES_BIT (RES4325_HT_AVAIL)));
      AND_REG (osh, &cc->max_res_mask,
	       ~(PMURES_BIT (RES4325_BBPLL_PWRSW_PU) |
		 PMURES_BIT (RES4325_HT_AVAIL)));

      /* Change the BBPLL drive strength to 2 for all channels */
      buf_strength = 0x222222;
      break;
    default:
      ASSERT (0);
    }
  SPINWAIT (R_REG (osh, &cc->clk_ctl_st) & CCS_HTAVAIL,
	    PMU_MAX_TRANSITION_DLY);
  ASSERT (!(R_REG (osh, &cc->clk_ctl_st) & CCS_HTAVAIL));

  PMU_MSG (("Done masking\n"));

  /* Write p1div and p2div to pllcontrol[0] */
  W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL0);
  tmp = R_REG (osh, &cc->pllcontrol_data) &
    ~(PMU1_PLL0_PC0_P1DIV_MASK | PMU1_PLL0_PC0_P2DIV_MASK);
  tmp |=
    ((xt->
      p1div << PMU1_PLL0_PC0_P1DIV_SHIFT) & PMU1_PLL0_PC0_P1DIV_MASK) | ((xt->
									  p2div
									  <<
									  PMU1_PLL0_PC0_P2DIV_SHIFT)
									 &
									 PMU1_PLL0_PC0_P2DIV_MASK);
  W_REG (osh, &cc->pllcontrol_data, tmp);

  /* Write ndiv_int and ndiv_mode to pllcontrol[2] */
  W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL2);
  tmp = R_REG (osh, &cc->pllcontrol_data) &
    ~(PMU1_PLL0_PC2_NDIV_INT_MASK | PMU1_PLL0_PC2_NDIV_MODE_MASK);
  tmp |=
    ((xt->
      ndiv_int << PMU1_PLL0_PC2_NDIV_INT_SHIFT) & PMU1_PLL0_PC2_NDIV_INT_MASK)
    | ((1 << PMU1_PLL0_PC2_NDIV_MODE_SHIFT) & PMU1_PLL0_PC2_NDIV_MODE_MASK);
  W_REG (osh, &cc->pllcontrol_data, tmp);

  /* Write ndiv_frac to pllcontrol[3] */
  W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL3);
  tmp = R_REG (osh, &cc->pllcontrol_data) & ~PMU1_PLL0_PC3_NDIV_FRAC_MASK;
  tmp |= ((xt->ndiv_frac << PMU1_PLL0_PC3_NDIV_FRAC_SHIFT) &
	  PMU1_PLL0_PC3_NDIV_FRAC_MASK);
  W_REG (osh, &cc->pllcontrol_data, tmp);

  if (buf_strength)
    {
      PMU_MSG (("Adjusting PLL buffer drive strength: %x\n", buf_strength));

      W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL5);
      tmp = R_REG (osh, &cc->pllcontrol_data) & ~PMU1_PLL0_PC5_CLK_DRV_MASK;
      tmp |= (buf_strength << PMU1_PLL0_PC5_CLK_DRV_SHIFT);
      W_REG (osh, &cc->pllcontrol_data, tmp);
    }

  PMU_MSG (("Done pll\n"));

  /* Write XtalFreq. Set the divisor also. */
  tmp = R_REG (osh, &cc->pmucontrol) &
    ~(PCTL_ILP_DIV_MASK | PCTL_XTALFREQ_MASK);
  tmp |= (((((xt->fref + 127) / 128) - 1) << PCTL_ILP_DIV_SHIFT) &
	  PCTL_ILP_DIV_MASK) |
    ((xt->xf << PCTL_XTALFREQ_SHIFT) & PCTL_XTALFREQ_MASK);
  W_REG (osh, &cc->pmucontrol, tmp);
}


static uint32
BCMINITFN (sb_pmu1_cpuclk0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc)
{
  const pmu1_xtaltab0_t *xt;
  uint32 xf, tmp, m1div;
#ifdef BCMDBG
  uint32 ndiv_int, ndiv_frac, p2div, p1div, fvco;
#endif

  /* Find the xtal frequency in the table */
  xf = (R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >>
    PCTL_XTALFREQ_SHIFT;
  for (xt = pmu1_xtaltab0; xt->fref; xt++)
    if (xt->xf == xf)
      break;
  if (xt->fref == 0)
    xt = &pmu1_xtaltab0[PMU1_XTAL0_DEFAULT];

  /* Read m1div from pllcontrol[1] */
  W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL1);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  m1div = (tmp & PMU1_PLL0_PC1_M1DIV_MASK) >> PMU1_PLL0_PC1_M1DIV_SHIFT;

#ifdef BCMDBG
  /* Read p2div/p1div from pllcontrol[0] */
  W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL0);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  p2div = (tmp & PMU1_PLL0_PC0_P2DIV_MASK) >> PMU1_PLL0_PC0_P2DIV_SHIFT;
  p1div = (tmp & PMU1_PLL0_PC0_P1DIV_MASK) >> PMU1_PLL0_PC0_P1DIV_SHIFT;

  /* Calculate Fvco based on xtal freq and ndiv and pdiv */
  W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL2);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  ndiv_int =
    (tmp & PMU1_PLL0_PC2_NDIV_INT_MASK) >> PMU1_PLL0_PC2_NDIV_INT_SHIFT;

  W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL3);
  tmp = R_REG (osh, &cc->pllcontrol_data);
  ndiv_frac =
    (tmp & PMU1_PLL0_PC3_NDIV_FRAC_MASK) >> PMU1_PLL0_PC3_NDIV_FRAC_SHIFT;

  fvco = (xt->fref * ndiv_int) << 8;
  fvco += (xt->fref * (ndiv_frac >> 12)) >> 4;
  fvco += (xt->fref * (ndiv_frac & 0xfff)) >> 12;
  fvco >>= 8;
  fvco *= p2div;
  fvco /= p1div;
  fvco /= 1000;
  fvco *= 1000;

  PMU_MSG (("sb_pmu0_cpuclk0: ndiv_int %u ndiv_frac %u "
	    "p2div %u p1div %u fvco %u\n",
	    ndiv_int, ndiv_frac, p2div, p1div, fvco));
  ASSERT (fvco == PMU1_PLL0_FVCO);
#endif /* BCMDBG */

  /* Return ARM/SB clock */
  return PMU1_PLL0_FVCO / m1div * 1000;
}

void BCMINITFN (sb_pmu_pll_init) (sb_t * sbh, osl_t * osh, uint xtalfreq)
{
  chipcregs_t *cc;
  uint origidx;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  /* Remember original core before switch to chipc */
  origidx = sb_coreidx (sbh);
  cc = sb_setcore (sbh, SB_CC, 0);
  ASSERT (cc);

  switch (sbh->chip)
    {
    case BCM4328_CHIP_ID:
      sb_pmu0_pllinit0 (sbh, osh, cc, xtalfreq);
      break;
    case BCM5354_CHIP_ID:
      sb_pmu0_pllinit0 (sbh, osh, cc, xtalfreq);
      break;
    case BCM4325_CHIP_ID:
      sb_pmu1_pllinit0 (sbh, osh, cc, xtalfreq);
      break;
    case BCM4312_CHIP_ID:
      sb_pmu1_pllinit0 (sbh, osh, cc, xtalfreq);
      break;
    default:
      PMU_MSG (("No PLL init done for chip %x rev %d pmurev %d\n",
		sbh->chip, sbh->chiprev, sbh->pmurev));
      break;
    }

  /* Return to original core */
  sb_setcoreidx (sbh, origidx);
}

uint32 BCMINITFN (sb_pmu_alp_clock) (sb_t * sbh, osl_t * osh)
{
  chipcregs_t *cc;
  uint origidx;
  uint32 clock = ALP_CLOCK;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  /* Remember original core before switch to chipc */
  origidx = sb_coreidx (sbh);
  cc = sb_setcore (sbh, SB_CC, 0);
  ASSERT (cc);

  switch (sbh->chip)
    {
    case BCM4328_CHIP_ID:
      clock = sb_pmu0_alpclk0 (sbh, osh, cc);
      break;
    case BCM5354_CHIP_ID:
      clock = sb_pmu0_alpclk0 (sbh, osh, cc);
      break;
    case BCM4325_CHIP_ID:
      clock = sb_pmu1_alpclk0 (sbh, osh, cc);
      break;
    case BCM4312_CHIP_ID:
      clock = sb_pmu1_alpclk0 (sbh, osh, cc);
      /* always 20Mhz */
      clock = 20000 * 1000;
      break;
    default:
      PMU_MSG (("No ALP clock specified "
		"for chip %x rev %d pmurev %d, using default %d Hz\n",
		sbh->chip, sbh->chiprev, sbh->pmurev, clock));
      break;
    }

  /* Return to original core */
  sb_setcoreidx (sbh, origidx);
  return clock;
}

uint BCMINITFN (sb_pmu_cpu_clock) (sb_t * sbh, osl_t * osh)
{
  chipcregs_t *cc;
  uint origidx;
  uint32 clock = HT_CLOCK;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  /* Remember original core before switch to chipc */
  origidx = sb_coreidx (sbh);
  cc = sb_setcore (sbh, SB_CC, 0);
  ASSERT (cc);

  switch (sbh->chip)
    {
    case BCM4328_CHIP_ID:
      clock = sb_pmu0_cpuclk0 (sbh, osh, cc);
      break;
    case BCM5354_CHIP_ID:
      clock = sb_pmu0_cpuclk0 (sbh, osh, cc);
      break;
    case BCM4325_CHIP_ID:
      clock = sb_pmu1_cpuclk0 (sbh, osh, cc);
      break;
    case BCM4312_CHIP_ID:
      clock = sb_pmu1_cpuclk0 (sbh, osh, cc);
      break;
    default:
      PMU_MSG (("No CPU clock specified "
		"for chip %x rev %d pmurev %d, using default %d Hz\n",
		sbh->chip, sbh->chiprev, sbh->pmurev, clock));
      break;
    }

  /* Return to original core */
  sb_setcoreidx (sbh, origidx);
  return clock;
}

void BCMINITFN (sb_pmu_init) (sb_t * sbh, osl_t * osh)
{
  chipcregs_t *cc;
  uint origidx;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  /* Remember original core before switch to chipc */
  origidx = sb_coreidx (sbh);
  cc = sb_setcore (sbh, SB_CC, 0);
  ASSERT (cc);

  if (sbh->pmurev >= 1)
    {
      if (sbh->chip == BCM4325_CHIP_ID && sbh->chiprev <= 1)
	AND_REG (osh, &cc->pmucontrol, ~PCTL_NOILP_ON_WAIT);
      else
	OR_REG (osh, &cc->pmucontrol, PCTL_NOILP_ON_WAIT);
    }

  /* Return to original core */
  sb_setcoreidx (sbh, origidx);
}

void BCMINITFN (sb_pmu_otp_power) (sb_t * sbh, osl_t * osh, bool on)
{
  chipcregs_t *cc;
  uint origidx;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  /* Remember original core before switch to chipc */
  origidx = sb_coreidx (sbh);
  cc = sb_setcore (sbh, SB_CC, 0);
  ASSERT (cc);

  switch (sbh->chip)
    {
    case BCM4325_CHIP_ID:
      if (on)
	{
	  OR_REG (osh, &cc->min_res_mask, PMURES_BIT (RES4325_LNLDO2_PU));
	  if (sbh->boardflags & BFL_BUCKBOOST)
	    AND_REG (osh, &cc->min_res_mask,
		     ~PMURES_BIT (RES4325_BUCK_BOOST_PWM));
	  OSL_DELAY (500);
	}
      else
	{
	  if (sbh->boardflags & BFL_BUCKBOOST)
	    OR_REG (osh, &cc->min_res_mask,
		    PMURES_BIT (RES4325_BUCK_BOOST_PWM));
	  AND_REG (osh, &cc->min_res_mask, ~PMURES_BIT (RES4325_LNLDO2_PU));
	}
      break;
    default:
      break;
    }

  /* Return to original core */
  sb_setcoreidx (sbh, origidx);
}

void
sb_pmu_rcal (sb_t * sbh, osl_t * osh)
{
  chipcregs_t *cc;
  uint origidx;

  ASSERT (sbh->cccaps & CC_CAP_PMU);

  /* Remember original core before switch to chipc */
  origidx = sb_coreidx (sbh);
  cc = sb_setcore (sbh, SB_CC, 0);
  ASSERT (cc);

  switch (sbh->chip)
    {
    case BCM4325_CHIP_ID:
      {
	uint8 rcal_code;
	uint32 val;

	/* Kick RCal */
	W_REG (osh, &cc->chipcontrol_addr, 1);
	AND_REG (osh, &cc->chipcontrol_data, ~0x04);
	OR_REG (osh, &cc->chipcontrol_data, 0x04);

	/* Wait for completion */
	SPINWAIT (0 == (R_REG (osh, &cc->chipstatus) & 0x08),
		  10 * 1000 * 1000);
	ASSERT (R_REG (osh, &cc->chipstatus) & 0x08);

	/* Drop the LSB to convert from 5 bit code to 4 bit code */
	rcal_code = (uint8) (R_REG (osh, &cc->chipstatus) >> 5) & 0x0f;
	PMU_MSG (("RCal completed, status 0x%x, code 0x%x\n",
		  R_REG (osh, &cc->chipstatus), rcal_code));

	/* Write RCal code into pmu_vreg_ctrl[32:29] */
	W_REG (osh, &cc->regcontrol_addr, 0);
	val = R_REG (osh, &cc->regcontrol_data) & ~((uint32) 0x07 << 29);
	val |= (uint32) (rcal_code & 0x07) << 29;
	W_REG (osh, &cc->regcontrol_data, val);
	W_REG (osh, &cc->regcontrol_addr, 1);
	val = R_REG (osh, &cc->regcontrol_data) & ~(uint32) 0x01;
	val |= (uint32) ((rcal_code >> 3) & 0x01);
	W_REG (osh, &cc->regcontrol_data, val);

	/* Write RCal code into pmu_chip_ctrl[33:30] */
	W_REG (osh, &cc->chipcontrol_addr, 0);
	val = R_REG (osh, &cc->chipcontrol_data) & ~((uint32) 0x03 << 30);
	val |= (uint32) (rcal_code & 0x03) << 30;
	W_REG (osh, &cc->chipcontrol_data, val);
	W_REG (osh, &cc->chipcontrol_addr, 1);
	val = R_REG (osh, &cc->chipcontrol_data) & ~(uint32) 0x03;
	val |= (uint32) ((rcal_code >> 2) & 0x03);
	W_REG (osh, &cc->chipcontrol_data, val);

	/* Set override in pmu_chip_ctrl[29] */
	W_REG (osh, &cc->chipcontrol_addr, 0);
	OR_REG (osh, &cc->chipcontrol_data, (0x01 << 29));

	/* Power off RCal block */
	W_REG (osh, &cc->chipcontrol_addr, 1);
	AND_REG (osh, &cc->chipcontrol_data, ~0x04);

	break;
      }
    default:
      break;
    }

  /* Return to original core */
  sb_setcoreidx (sbh, origidx);
}