diff options
-rw-r--r-- | drivers/watchdog/sp5100_tco.c | 169 | ||||
-rw-r--r-- | drivers/watchdog/sp5100_tco.h | 21 |
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 | ||
48 | enum tco_reg_layout { | ||
49 | sp5100, sb800, efch | ||
50 | }; | ||
51 | |||
43 | struct sp5100_tco { | 52 | struct 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 | ||
70 | static bool tco_has_sp5100_reg_layout(struct pci_dev *dev) | 80 | static 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 | ||
76 | static int tco_timer_start(struct watchdog_device *wdd) | 96 | static 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 | ||
142 | static void tco_timer_enable(void) | 162 | static 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 | ||