aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/watchdog/sp5100_tco.c169
-rw-r--r--drivers/watchdog/sp5100_tco.h21
2 files changed, 133 insertions, 57 deletions
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c
index 23246cb40598..41aaae2d5287 100644
--- a/drivers/watchdog/sp5100_tco.c
+++ b/drivers/watchdog/sp5100_tco.c
@@ -16,6 +16,11 @@
16 * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide", 16 * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide",
17 * AMD Publication 45482 "AMD SB800-Series Southbridges Register 17 * AMD Publication 45482 "AMD SB800-Series Southbridges Register
18 * Reference Guide" 18 * Reference Guide"
19 * AMD Publication 48751 "BIOS and Kernel Developer’s Guide (BKDG)
20 * for AMD Family 16h Models 00h-0Fh Processors"
21 * AMD Publication 51192 "AMD Bolton FCH Register Reference Guide"
22 * AMD Publication 52740 "BIOS and Kernel Developer’s Guide (BKDG)
23 * for AMD Family 16h Models 30h-3Fh Processors"
19 */ 24 */
20 25
21/* 26/*
@@ -40,9 +45,14 @@
40 45
41/* internal variables */ 46/* internal variables */
42 47
48enum tco_reg_layout {
49 sp5100, sb800, efch
50};
51
43struct sp5100_tco { 52struct sp5100_tco {
44 struct watchdog_device wdd; 53 struct watchdog_device wdd;
45 void __iomem *tcobase; 54 void __iomem *tcobase;
55 enum tco_reg_layout tco_reg_layout;
46}; 56};
47 57
48/* the watchdog platform device */ 58/* the watchdog platform device */
@@ -67,10 +77,20 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started."
67 * Some TCO specific functions 77 * Some TCO specific functions
68 */ 78 */
69 79
70static bool tco_has_sp5100_reg_layout(struct pci_dev *dev) 80static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev)
71{ 81{
72 return dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && 82 if (dev->vendor == PCI_VENDOR_ID_ATI &&
73 dev->revision < 0x40; 83 dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
84 dev->revision < 0x40) {
85 return sp5100;
86 } else if (dev->vendor == PCI_VENDOR_ID_AMD &&
87 ((dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
88 dev->revision >= 0x41) ||
89 (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
90 dev->revision >= 0x49))) {
91 return efch;
92 }
93 return sb800;
74} 94}
75 95
76static int tco_timer_start(struct watchdog_device *wdd) 96static int tco_timer_start(struct watchdog_device *wdd)
@@ -139,9 +159,12 @@ static void sp5100_tco_update_pm_reg8(u8 index, u8 reset, u8 set)
139 outb(val, SP5100_IO_PM_DATA_REG); 159 outb(val, SP5100_IO_PM_DATA_REG);
140} 160}
141 161
142static void tco_timer_enable(void) 162static void tco_timer_enable(struct sp5100_tco *tco)
143{ 163{
144 if (!tco_has_sp5100_reg_layout(sp5100_tco_pci)) { 164 u32 val;
165
166 switch (tco->tco_reg_layout) {
167 case sb800:
145 /* For SB800 or later */ 168 /* For SB800 or later */
146 /* Set the Watchdog timer resolution to 1 sec */ 169 /* Set the Watchdog timer resolution to 1 sec */
147 sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONFIG, 170 sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONFIG,
@@ -151,9 +174,8 @@ static void tco_timer_enable(void)
151 sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONTROL, 174 sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONTROL,
152 ~SB800_PM_WATCHDOG_DISABLE, 175 ~SB800_PM_WATCHDOG_DISABLE,
153 SB800_PCI_WATCHDOG_DECODE_EN); 176 SB800_PCI_WATCHDOG_DECODE_EN);
154 } else { 177 break;
155 u32 val; 178 case sp5100:
156
157 /* For SP5100 or SB7x0 */ 179 /* For SP5100 or SB7x0 */
158 /* Enable watchdog decode bit */ 180 /* Enable watchdog decode bit */
159 pci_read_config_dword(sp5100_tco_pci, 181 pci_read_config_dword(sp5100_tco_pci,
@@ -170,6 +192,13 @@ static void tco_timer_enable(void)
170 sp5100_tco_update_pm_reg8(SP5100_PM_WATCHDOG_CONTROL, 192 sp5100_tco_update_pm_reg8(SP5100_PM_WATCHDOG_CONTROL,
171 ~SP5100_PM_WATCHDOG_DISABLE, 193 ~SP5100_PM_WATCHDOG_DISABLE,
172 SP5100_PM_WATCHDOG_SECOND_RES); 194 SP5100_PM_WATCHDOG_SECOND_RES);
195 break;
196 case efch:
197 /* Set the Watchdog timer resolution to 1 sec and enable */
198 sp5100_tco_update_pm_reg8(EFCH_PM_DECODEEN3,
199 ~EFCH_PM_WATCHDOG_DISABLE,
200 EFCH_PM_DECODEEN_SECOND_RES);
201 break;
173 } 202 }
174} 203}
175 204
@@ -189,89 +218,113 @@ static int sp5100_tco_setupdevice(struct device *dev,
189{ 218{
190 struct sp5100_tco *tco = watchdog_get_drvdata(wdd); 219 struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
191 const char *dev_name; 220 const char *dev_name;
192 u8 base_addr; 221 u32 mmio_addr = 0, val;
193 u32 val;
194 int ret; 222 int ret;
195 223
196 /*
197 * Determine type of southbridge chipset.
198 */
199 if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) {
200 dev_name = SP5100_DEVNAME;
201 base_addr = SP5100_PM_WATCHDOG_BASE;
202 } else {
203 dev_name = SB800_DEVNAME;
204 base_addr = SB800_PM_WATCHDOG_BASE;
205 }
206
207 /* Request the IO ports used by this driver */ 224 /* Request the IO ports used by this driver */
208 if (!request_muxed_region(SP5100_IO_PM_INDEX_REG, 225 if (!request_muxed_region(SP5100_IO_PM_INDEX_REG,
209 SP5100_PM_IOPORTS_SIZE, dev_name)) { 226 SP5100_PM_IOPORTS_SIZE, "sp5100_tco")) {
210 dev_err(dev, "I/O address 0x%04x already in use\n", 227 dev_err(dev, "I/O address 0x%04x already in use\n",
211 SP5100_IO_PM_INDEX_REG); 228 SP5100_IO_PM_INDEX_REG);
212 return -EBUSY; 229 return -EBUSY;
213 } 230 }
214 231
215 /* 232 /*
216 * First, Find the watchdog timer MMIO address from indirect I/O. 233 * Determine type of southbridge chipset.
217 * Low three bits of BASE are reserved.
218 */ 234 */
219 val = sp5100_tco_read_pm_reg32(base_addr) & 0xfffffff8; 235 switch (tco->tco_reg_layout) {
220 236 case sp5100:
221 dev_dbg(dev, "Got 0x%04x from indirect I/O\n", val); 237 dev_name = SP5100_DEVNAME;
238 mmio_addr = sp5100_tco_read_pm_reg32(SP5100_PM_WATCHDOG_BASE) &
239 0xfffffff8;
240 break;
241 case sb800:
242 dev_name = SB800_DEVNAME;
243 mmio_addr = sp5100_tco_read_pm_reg32(SB800_PM_WATCHDOG_BASE) &
244 0xfffffff8;
245 break;
246 case efch:
247 dev_name = SB800_DEVNAME;
248 val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN);
249 if (val & EFCH_PM_DECODEEN_WDT_TMREN)
250 mmio_addr = EFCH_PM_WDT_ADDR;
251 break;
252 default:
253 return -ENODEV;
254 }
222 255
223 /* Check MMIO address conflict */ 256 /* Check MMIO address conflict */
224 if (!devm_request_mem_region(dev, val, SP5100_WDT_MEM_MAP_SIZE, 257 if (!mmio_addr ||
258 !devm_request_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE,
225 dev_name)) { 259 dev_name)) {
226 dev_dbg(dev, "MMIO address 0x%04x already in use\n", val); 260 if (mmio_addr)
227 /* 261 dev_dbg(dev, "MMIO address 0x%08x already in use\n",
228 * Secondly, Find the watchdog timer MMIO address 262 mmio_addr);
229 * from SBResource_MMIO register. 263 switch (tco->tco_reg_layout) {
230 */ 264 case sp5100:
231 if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) { 265 /*
266 * Secondly, Find the watchdog timer MMIO address
267 * from SBResource_MMIO register.
268 */
232 /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ 269 /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */
233 pci_read_config_dword(sp5100_tco_pci, 270 pci_read_config_dword(sp5100_tco_pci,
234 SP5100_SB_RESOURCE_MMIO_BASE, 271 SP5100_SB_RESOURCE_MMIO_BASE,
235 &val); 272 &mmio_addr);
236 } else { 273 if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN |
274 SB800_ACPI_MMIO_SEL)) !=
275 SB800_ACPI_MMIO_DECODE_EN) {
276 ret = -ENODEV;
277 goto unreg_region;
278 }
279 mmio_addr &= ~0xFFF;
280 mmio_addr += SB800_PM_WDT_MMIO_OFFSET;
281 break;
282 case sb800:
237 /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ 283 /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */
238 val = sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN); 284 mmio_addr =
239 } 285 sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN);
240 286 if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN |
241 /* The SBResource_MMIO is enabled and mapped memory space? */ 287 SB800_ACPI_MMIO_SEL)) !=
242 if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) !=
243 SB800_ACPI_MMIO_DECODE_EN) { 288 SB800_ACPI_MMIO_DECODE_EN) {
244 dev_notice(dev, 289 ret = -ENODEV;
245 "failed to find MMIO address, giving up.\n"); 290 goto unreg_region;
246 ret = -ENODEV; 291 }
247 goto unreg_region; 292 mmio_addr &= ~0xFFF;
293 mmio_addr += SB800_PM_WDT_MMIO_OFFSET;
294 break;
295 case efch:
296 val = sp5100_tco_read_pm_reg8(EFCH_PM_ISACONTROL);
297 if (!(val & EFCH_PM_ISACONTROL_MMIOEN)) {
298 ret = -ENODEV;
299 goto unreg_region;
300 }
301 mmio_addr = EFCH_PM_ACPI_MMIO_ADDR +
302 EFCH_PM_ACPI_MMIO_WDT_OFFSET;
303 break;
248 } 304 }
249 /* Clear unnecessary the low twelve bits */ 305 dev_dbg(dev, "Got 0x%08x from SBResource_MMIO register\n",
250 val &= ~0xFFF; 306 mmio_addr);
251 /* Add the Watchdog Timer offset to base address. */ 307 if (!devm_request_mem_region(dev, mmio_addr,
252 val += SB800_PM_WDT_MMIO_OFFSET; 308 SP5100_WDT_MEM_MAP_SIZE,
253 /* Check MMIO address conflict */
254 if (!devm_request_mem_region(dev, val, SP5100_WDT_MEM_MAP_SIZE,
255 dev_name)) { 309 dev_name)) {
256 dev_dbg(dev, "MMIO address 0x%04x already in use\n", 310 dev_dbg(dev, "MMIO address 0x%08x already in use\n",
257 val); 311 mmio_addr);
258 ret = -EBUSY; 312 ret = -EBUSY;
259 goto unreg_region; 313 goto unreg_region;
260 } 314 }
261 dev_dbg(dev, "Got 0x%04x from SBResource_MMIO register\n", val);
262 } 315 }
263 316
264 tco->tcobase = devm_ioremap(dev, val, SP5100_WDT_MEM_MAP_SIZE); 317 tco->tcobase = devm_ioremap(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE);
265 if (!tco->tcobase) { 318 if (!tco->tcobase) {
266 dev_err(dev, "failed to get tcobase address\n"); 319 dev_err(dev, "failed to get tcobase address\n");
267 ret = -ENOMEM; 320 ret = -ENOMEM;
268 goto unreg_region; 321 goto unreg_region;
269 } 322 }
270 323
271 dev_info(dev, "Using 0x%04x for watchdog MMIO address\n", val); 324 dev_info(dev, "Using 0x%08x for watchdog MMIO address\n", mmio_addr);
272 325
273 /* Setup the watchdog timer */ 326 /* Setup the watchdog timer */
274 tco_timer_enable(); 327 tco_timer_enable(tco);
275 328
276 val = readl(SP5100_WDT_CONTROL(tco->tcobase)); 329 val = readl(SP5100_WDT_CONTROL(tco->tcobase));
277 if (val & SP5100_WDT_DISABLED) { 330 if (val & SP5100_WDT_DISABLED) {
@@ -332,6 +385,8 @@ static int sp5100_tco_probe(struct platform_device *pdev)
332 if (!tco) 385 if (!tco)
333 return -ENOMEM; 386 return -ENOMEM;
334 387
388 tco->tco_reg_layout = tco_reg_layout(sp5100_tco_pci);
389
335 wdd = &tco->wdd; 390 wdd = &tco->wdd;
336 wdd->parent = dev; 391 wdd->parent = dev;
337 wdd->info = &sp5100_tco_wdt_info; 392 wdd->info = &sp5100_tco_wdt_info;
diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h
index 008b2094bd13..87eaf357ae01 100644
--- a/drivers/watchdog/sp5100_tco.h
+++ b/drivers/watchdog/sp5100_tco.h
@@ -62,3 +62,24 @@
62#define SB800_PM_WDT_MMIO_OFFSET 0xB00 62#define SB800_PM_WDT_MMIO_OFFSET 0xB00
63 63
64#define SB800_DEVNAME "SB800 TCO" 64#define SB800_DEVNAME "SB800 TCO"
65
66/* For recent chips with embedded FCH (rev 40+) */
67
68#define EFCH_PM_DECODEEN 0x00
69
70#define EFCH_PM_DECODEEN_WDT_TMREN BIT(7)
71
72
73#define EFCH_PM_DECODEEN3 0x00
74#define EFCH_PM_DECODEEN_SECOND_RES GENMASK(1, 0)
75#define EFCH_PM_WATCHDOG_DISABLE ((u8)GENMASK(3, 2))
76
77/* WDT MMIO if enabled with PM00_DECODEEN_WDT_TMREN */
78#define EFCH_PM_WDT_ADDR 0xfeb00000
79
80#define EFCH_PM_ISACONTROL 0x04
81
82#define EFCH_PM_ISACONTROL_MMIOEN BIT(1)
83
84#define EFCH_PM_ACPI_MMIO_ADDR 0xfed80000
85#define EFCH_PM_ACPI_MMIO_WDT_OFFSET 0x00000b00