113014ca0SSøren Schmidt /*- 213014ca0SSøren Schmidt * Copyright (c) 1998 - 2008 S�ren Schmidt <sos@FreeBSD.org> 313014ca0SSøren Schmidt * All rights reserved. 413014ca0SSøren Schmidt * 513014ca0SSøren Schmidt * Redistribution and use in source and binary forms, with or without 613014ca0SSøren Schmidt * modification, are permitted provided that the following conditions 713014ca0SSøren Schmidt * are met: 813014ca0SSøren Schmidt * 1. Redistributions of source code must retain the above copyright 913014ca0SSøren Schmidt * notice, this list of conditions and the following disclaimer, 1013014ca0SSøren Schmidt * without modification, immediately at the beginning of the file. 1113014ca0SSøren Schmidt * 2. Redistributions in binary form must reproduce the above copyright 1213014ca0SSøren Schmidt * notice, this list of conditions and the following disclaimer in the 1313014ca0SSøren Schmidt * documentation and/or other materials provided with the distribution. 1413014ca0SSøren Schmidt * 1513014ca0SSøren Schmidt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1613014ca0SSøren Schmidt * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1713014ca0SSøren Schmidt * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1813014ca0SSøren Schmidt * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1913014ca0SSøren Schmidt * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2013014ca0SSøren Schmidt * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2113014ca0SSøren Schmidt * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2213014ca0SSøren Schmidt * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2313014ca0SSøren Schmidt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2413014ca0SSøren Schmidt * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2513014ca0SSøren Schmidt */ 2613014ca0SSøren Schmidt 2713014ca0SSøren Schmidt #include <sys/cdefs.h> 2813014ca0SSøren Schmidt __FBSDID("$FreeBSD$"); 2913014ca0SSøren Schmidt 3013014ca0SSøren Schmidt #include "opt_ata.h" 3113014ca0SSøren Schmidt #include <sys/param.h> 3213014ca0SSøren Schmidt #include <sys/systm.h> 3313014ca0SSøren Schmidt #include <sys/kernel.h> 3413014ca0SSøren Schmidt #include <sys/ata.h> 3513014ca0SSøren Schmidt #include <sys/bus.h> 3613014ca0SSøren Schmidt #include <sys/endian.h> 3713014ca0SSøren Schmidt #include <sys/malloc.h> 3813014ca0SSøren Schmidt #include <sys/lock.h> 3913014ca0SSøren Schmidt #include <sys/mutex.h> 4013014ca0SSøren Schmidt #include <sys/sema.h> 4113014ca0SSøren Schmidt #include <sys/taskqueue.h> 4213014ca0SSøren Schmidt #include <vm/uma.h> 4313014ca0SSøren Schmidt #include <machine/stdarg.h> 4413014ca0SSøren Schmidt #include <machine/resource.h> 4513014ca0SSøren Schmidt #include <machine/bus.h> 4613014ca0SSøren Schmidt #include <sys/rman.h> 4713014ca0SSøren Schmidt #include <dev/ata/ata-all.h> 4813014ca0SSøren Schmidt #include <ata_if.h> 4913014ca0SSøren Schmidt 5013014ca0SSøren Schmidt void 5113014ca0SSøren Schmidt ata_sata_phy_check_events(device_t dev) 5213014ca0SSøren Schmidt { 5313014ca0SSøren Schmidt struct ata_channel *ch = device_get_softc(dev); 5413014ca0SSøren Schmidt u_int32_t error = ATA_IDX_INL(ch, ATA_SERROR); 5513014ca0SSøren Schmidt 5613014ca0SSøren Schmidt /* clear error bits/interrupt */ 5713014ca0SSøren Schmidt ATA_IDX_OUTL(ch, ATA_SERROR, error); 5813014ca0SSøren Schmidt 5913014ca0SSøren Schmidt /* if we have a connection event deal with it */ 604c10f2e6SAlexander Motin if ((error & ATA_SE_PHY_CHANGED) && (ch->pm_level == 0)) { 616030a3f0SAlexander Motin if (bootverbose) { 626030a3f0SAlexander Motin u_int32_t status = ATA_IDX_INL(ch, ATA_SSTATUS); 6313014ca0SSøren Schmidt if (((status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN1) || 6413014ca0SSøren Schmidt ((status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN2)) { 6513014ca0SSøren Schmidt device_printf(dev, "CONNECT requested\n"); 666030a3f0SAlexander Motin } else 6713014ca0SSøren Schmidt device_printf(dev, "DISCONNECT requested\n"); 6813014ca0SSøren Schmidt } 696030a3f0SAlexander Motin taskqueue_enqueue(taskqueue_thread, &ch->conntask); 7013014ca0SSøren Schmidt } 7113014ca0SSøren Schmidt } 7213014ca0SSøren Schmidt 739cf4fe2eSAlexander Motin int 749cf4fe2eSAlexander Motin ata_sata_scr_read(struct ata_channel *ch, int port, int reg, uint32_t *val) 759cf4fe2eSAlexander Motin { 769cf4fe2eSAlexander Motin int r; 779cf4fe2eSAlexander Motin 789cf4fe2eSAlexander Motin if (port < 0) { 799cf4fe2eSAlexander Motin *val = ATA_IDX_INL(ch, reg); 809cf4fe2eSAlexander Motin return (0); 819cf4fe2eSAlexander Motin } else { 829cf4fe2eSAlexander Motin switch (reg) { 839cf4fe2eSAlexander Motin case ATA_SSTATUS: 849cf4fe2eSAlexander Motin r = 0; 859cf4fe2eSAlexander Motin break; 869cf4fe2eSAlexander Motin case ATA_SERROR: 879cf4fe2eSAlexander Motin r = 1; 889cf4fe2eSAlexander Motin break; 899cf4fe2eSAlexander Motin case ATA_SCONTROL: 909cf4fe2eSAlexander Motin r = 2; 919cf4fe2eSAlexander Motin break; 929cf4fe2eSAlexander Motin default: 939cf4fe2eSAlexander Motin return (EINVAL); 949cf4fe2eSAlexander Motin } 959cf4fe2eSAlexander Motin return (ch->hw.pm_read(ch->dev, port, r, val)); 969cf4fe2eSAlexander Motin } 979cf4fe2eSAlexander Motin } 989cf4fe2eSAlexander Motin 999cf4fe2eSAlexander Motin int 1009cf4fe2eSAlexander Motin ata_sata_scr_write(struct ata_channel *ch, int port, int reg, uint32_t val) 1019cf4fe2eSAlexander Motin { 1029cf4fe2eSAlexander Motin int r; 1039cf4fe2eSAlexander Motin 1049cf4fe2eSAlexander Motin if (port < 0) { 1059cf4fe2eSAlexander Motin ATA_IDX_OUTL(ch, reg, val); 1069cf4fe2eSAlexander Motin return (0); 1079cf4fe2eSAlexander Motin } else { 1089cf4fe2eSAlexander Motin switch (reg) { 1099cf4fe2eSAlexander Motin case ATA_SERROR: 1109cf4fe2eSAlexander Motin r = 1; 1119cf4fe2eSAlexander Motin break; 1129cf4fe2eSAlexander Motin case ATA_SCONTROL: 1139cf4fe2eSAlexander Motin r = 2; 1149cf4fe2eSAlexander Motin break; 1159cf4fe2eSAlexander Motin default: 1169cf4fe2eSAlexander Motin return (EINVAL); 1179cf4fe2eSAlexander Motin } 1189cf4fe2eSAlexander Motin return (ch->hw.pm_write(ch->dev, port, r, val)); 1199cf4fe2eSAlexander Motin } 1209cf4fe2eSAlexander Motin } 1219cf4fe2eSAlexander Motin 12213014ca0SSøren Schmidt static int 1239cf4fe2eSAlexander Motin ata_sata_connect(struct ata_channel *ch, int port) 12413014ca0SSøren Schmidt { 12513014ca0SSøren Schmidt u_int32_t status; 12613014ca0SSøren Schmidt int timeout; 12713014ca0SSøren Schmidt 12813014ca0SSøren Schmidt /* wait up to 1 second for "connect well" */ 12913014ca0SSøren Schmidt for (timeout = 0; timeout < 100 ; timeout++) { 1309cf4fe2eSAlexander Motin if (ata_sata_scr_read(ch, port, ATA_SSTATUS, &status)) 1319cf4fe2eSAlexander Motin return (0); 13213014ca0SSøren Schmidt if ((status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN1 || 13313014ca0SSøren Schmidt (status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN2) 13413014ca0SSøren Schmidt break; 13513014ca0SSøren Schmidt ata_udelay(10000); 13613014ca0SSøren Schmidt } 13713014ca0SSøren Schmidt if (timeout >= 100) { 1389cf4fe2eSAlexander Motin if (bootverbose) { 1399cf4fe2eSAlexander Motin if (port < 0) { 1409cf4fe2eSAlexander Motin device_printf(ch->dev, "SATA connect timeout status=%08x\n", 1419cf4fe2eSAlexander Motin status); 1429cf4fe2eSAlexander Motin } else { 1439cf4fe2eSAlexander Motin device_printf(ch->dev, "p%d: SATA connect timeout status=%08x\n", 1449cf4fe2eSAlexander Motin port, status); 1459cf4fe2eSAlexander Motin } 1469cf4fe2eSAlexander Motin } 14713014ca0SSøren Schmidt return 0; 14813014ca0SSøren Schmidt } 1499cf4fe2eSAlexander Motin if (bootverbose) { 1509cf4fe2eSAlexander Motin if (port < 0) { 1519cf4fe2eSAlexander Motin device_printf(ch->dev, "SATA connect time=%dms status=%08x\n", 1529cf4fe2eSAlexander Motin timeout * 10, status); 1539cf4fe2eSAlexander Motin } else { 1549cf4fe2eSAlexander Motin device_printf(ch->dev, "p%d: SATA connect time=%dms status=%08x\n", 1559cf4fe2eSAlexander Motin port, timeout * 10, status); 1569cf4fe2eSAlexander Motin } 1579cf4fe2eSAlexander Motin } 15813014ca0SSøren Schmidt 15913014ca0SSøren Schmidt /* clear SATA error register */ 1609cf4fe2eSAlexander Motin ata_sata_scr_write(ch, port, ATA_SERROR, 0xffffffff); 16113014ca0SSøren Schmidt 16213014ca0SSøren Schmidt return 1; 16313014ca0SSøren Schmidt } 16413014ca0SSøren Schmidt 16513014ca0SSøren Schmidt int 1669cf4fe2eSAlexander Motin ata_sata_phy_reset(device_t dev, int port, int quick) 16713014ca0SSøren Schmidt { 16813014ca0SSøren Schmidt struct ata_channel *ch = device_get_softc(dev); 16913014ca0SSøren Schmidt int loop, retry; 1709cf4fe2eSAlexander Motin uint32_t val; 17113014ca0SSøren Schmidt 1729cf4fe2eSAlexander Motin if (quick) { 1739cf4fe2eSAlexander Motin if (ata_sata_scr_read(ch, port, ATA_SCONTROL, &val)) 1749cf4fe2eSAlexander Motin return (0); 1759cf4fe2eSAlexander Motin if ((val & ATA_SC_DET_MASK) == ATA_SC_DET_IDLE) 1769cf4fe2eSAlexander Motin return ata_sata_connect(ch, port); 1779cf4fe2eSAlexander Motin } 17813014ca0SSøren Schmidt 1799cf4fe2eSAlexander Motin if (bootverbose) { 1809cf4fe2eSAlexander Motin if (port < 0) { 1819cf4fe2eSAlexander Motin device_printf(dev, "hardware reset ...\n"); 1829cf4fe2eSAlexander Motin } else { 1839cf4fe2eSAlexander Motin device_printf(dev, "p%d: hardware reset ...\n", port); 1849cf4fe2eSAlexander Motin } 1859cf4fe2eSAlexander Motin } 18613014ca0SSøren Schmidt for (retry = 0; retry < 10; retry++) { 18713014ca0SSøren Schmidt for (loop = 0; loop < 10; loop++) { 1889cf4fe2eSAlexander Motin if (ata_sata_scr_write(ch, port, ATA_SCONTROL, ATA_SC_DET_RESET)) 1899cf4fe2eSAlexander Motin return (0); 19013014ca0SSøren Schmidt ata_udelay(100); 1919cf4fe2eSAlexander Motin if (ata_sata_scr_read(ch, port, ATA_SCONTROL, &val)) 1929cf4fe2eSAlexander Motin return (0); 1939cf4fe2eSAlexander Motin if ((val & ATA_SC_DET_MASK) == ATA_SC_DET_RESET) 19413014ca0SSøren Schmidt break; 19513014ca0SSøren Schmidt } 19613014ca0SSøren Schmidt ata_udelay(5000); 19713014ca0SSøren Schmidt for (loop = 0; loop < 10; loop++) { 1989cf4fe2eSAlexander Motin if (ata_sata_scr_write(ch, port, ATA_SCONTROL, 1994c10f2e6SAlexander Motin ATA_SC_DET_IDLE | ((ch->pm_level > 0) ? 0 : 2004c10f2e6SAlexander Motin ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER))) 2019cf4fe2eSAlexander Motin return (0); 20213014ca0SSøren Schmidt ata_udelay(100); 2039cf4fe2eSAlexander Motin if (ata_sata_scr_read(ch, port, ATA_SCONTROL, &val)) 2049cf4fe2eSAlexander Motin return (0); 2059cf4fe2eSAlexander Motin if ((val & ATA_SC_DET_MASK) == 0) 2069cf4fe2eSAlexander Motin return ata_sata_connect(ch, port); 20713014ca0SSøren Schmidt } 20813014ca0SSøren Schmidt } 20913014ca0SSøren Schmidt return 0; 21013014ca0SSøren Schmidt } 21113014ca0SSøren Schmidt 212066f913aSAlexander Motin int 213066f913aSAlexander Motin ata_sata_setmode(device_t dev, int target, int mode) 21413014ca0SSøren Schmidt { 21513014ca0SSøren Schmidt 216066f913aSAlexander Motin return (min(mode, ATA_UDMA5)); 21713014ca0SSøren Schmidt } 218066f913aSAlexander Motin 219066f913aSAlexander Motin int 220066f913aSAlexander Motin ata_sata_getrev(device_t dev, int target) 221066f913aSAlexander Motin { 222066f913aSAlexander Motin struct ata_channel *ch = device_get_softc(dev); 223066f913aSAlexander Motin 224066f913aSAlexander Motin if (ch->r_io[ATA_SSTATUS].res) 225066f913aSAlexander Motin return ((ATA_IDX_INL(ch, ATA_SSTATUS) & 0x0f0) >> 4); 2266a5d28b9SAlexander Motin return (0xff); 22713014ca0SSøren Schmidt } 22813014ca0SSøren Schmidt 22913014ca0SSøren Schmidt int 23013014ca0SSøren Schmidt ata_request2fis_h2d(struct ata_request *request, u_int8_t *fis) 23113014ca0SSøren Schmidt { 23213014ca0SSøren Schmidt 23313014ca0SSøren Schmidt if (request->flags & ATA_R_ATAPI) { 23413014ca0SSøren Schmidt fis[0] = 0x27; /* host to device */ 235ebbb35baSAlexander Motin fis[1] = 0x80 | (request->unit & 0x0f); 23613014ca0SSøren Schmidt fis[2] = ATA_PACKET_CMD; 23713014ca0SSøren Schmidt if (request->flags & (ATA_R_READ | ATA_R_WRITE)) 23813014ca0SSøren Schmidt fis[3] = ATA_F_DMA; 23913014ca0SSøren Schmidt else { 24013014ca0SSøren Schmidt fis[5] = request->transfersize; 24113014ca0SSøren Schmidt fis[6] = request->transfersize >> 8; 24213014ca0SSøren Schmidt } 24313014ca0SSøren Schmidt fis[7] = ATA_D_LBA; 24413014ca0SSøren Schmidt fis[15] = ATA_A_4BIT; 24513014ca0SSøren Schmidt return 20; 24613014ca0SSøren Schmidt } 24713014ca0SSøren Schmidt else { 24813014ca0SSøren Schmidt fis[0] = 0x27; /* host to device */ 249ebbb35baSAlexander Motin fis[1] = 0x80 | (request->unit & 0x0f); 25013014ca0SSøren Schmidt fis[2] = request->u.ata.command; 25113014ca0SSøren Schmidt fis[3] = request->u.ata.feature; 25213014ca0SSøren Schmidt fis[4] = request->u.ata.lba; 25313014ca0SSøren Schmidt fis[5] = request->u.ata.lba >> 8; 25413014ca0SSøren Schmidt fis[6] = request->u.ata.lba >> 16; 25513014ca0SSøren Schmidt fis[7] = ATA_D_LBA; 256ebbb35baSAlexander Motin if (!(request->flags & ATA_R_48BIT)) 25713014ca0SSøren Schmidt fis[7] |= (ATA_D_IBM | (request->u.ata.lba >> 24 & 0x0f)); 25813014ca0SSøren Schmidt fis[8] = request->u.ata.lba >> 24; 25913014ca0SSøren Schmidt fis[9] = request->u.ata.lba >> 32; 26013014ca0SSøren Schmidt fis[10] = request->u.ata.lba >> 40; 26113014ca0SSøren Schmidt fis[11] = request->u.ata.feature >> 8; 26213014ca0SSøren Schmidt fis[12] = request->u.ata.count; 26313014ca0SSøren Schmidt fis[13] = request->u.ata.count >> 8; 26413014ca0SSøren Schmidt fis[15] = ATA_A_4BIT; 26513014ca0SSøren Schmidt return 20; 26613014ca0SSøren Schmidt } 26713014ca0SSøren Schmidt return 0; 26813014ca0SSøren Schmidt } 26913014ca0SSøren Schmidt 27013014ca0SSøren Schmidt void 27113014ca0SSøren Schmidt ata_pm_identify(device_t dev) 27213014ca0SSøren Schmidt { 27313014ca0SSøren Schmidt struct ata_channel *ch = device_get_softc(dev); 27413014ca0SSøren Schmidt u_int32_t pm_chipid, pm_revision, pm_ports; 27513014ca0SSøren Schmidt int port; 27613014ca0SSøren Schmidt 27713014ca0SSøren Schmidt /* get PM vendor & product data */ 27813014ca0SSøren Schmidt if (ch->hw.pm_read(dev, ATA_PM, 0, &pm_chipid)) { 27913014ca0SSøren Schmidt device_printf(dev, "error getting PM vendor data\n"); 28013014ca0SSøren Schmidt return; 28113014ca0SSøren Schmidt } 28213014ca0SSøren Schmidt 28313014ca0SSøren Schmidt /* get PM revision data */ 28413014ca0SSøren Schmidt if (ch->hw.pm_read(dev, ATA_PM, 1, &pm_revision)) { 28513014ca0SSøren Schmidt device_printf(dev, "error getting PM revison data\n"); 28613014ca0SSøren Schmidt return; 28713014ca0SSøren Schmidt } 28813014ca0SSøren Schmidt 28913014ca0SSøren Schmidt /* get number of HW ports on the PM */ 29013014ca0SSøren Schmidt if (ch->hw.pm_read(dev, ATA_PM, 2, &pm_ports)) { 29113014ca0SSøren Schmidt device_printf(dev, "error getting PM port info\n"); 29213014ca0SSøren Schmidt return; 29313014ca0SSøren Schmidt } 29413014ca0SSøren Schmidt pm_ports &= 0x0000000f; 29513014ca0SSøren Schmidt 29613014ca0SSøren Schmidt /* chip specific quirks */ 29713014ca0SSøren Schmidt switch (pm_chipid) { 29813014ca0SSøren Schmidt case 0x37261095: 2999cf4fe2eSAlexander Motin /* This PM declares 6 ports, while only 5 of them are real. 3009cf4fe2eSAlexander Motin * Port 5 is enclosure management bridge port, which has implementation 3019cf4fe2eSAlexander Motin * problems, causing probe faults. Hide it for now. */ 3029cf4fe2eSAlexander Motin device_printf(dev, "SiI 3726 (rev=%x) Port Multiplier with %d (5) ports\n", 30313014ca0SSøren Schmidt pm_revision, pm_ports); 3049cf4fe2eSAlexander Motin pm_ports = 5; 3059cf4fe2eSAlexander Motin break; 3069cf4fe2eSAlexander Motin 3079cf4fe2eSAlexander Motin case 0x47261095: 3089cf4fe2eSAlexander Motin /* This PM declares 7 ports, while only 5 of them are real. 3099cf4fe2eSAlexander Motin * Port 5 is some fake "Config Disk" with 640 sectors size, 3109cf4fe2eSAlexander Motin * port 6 is enclosure management bridge port. 3119cf4fe2eSAlexander Motin * Both fake ports has implementation problems, causing 3129cf4fe2eSAlexander Motin * probe faults. Hide them for now. */ 3139cf4fe2eSAlexander Motin device_printf(dev, "SiI 4726 (rev=%x) Port Multiplier with %d (5) ports\n", 3149cf4fe2eSAlexander Motin pm_revision, pm_ports); 3159cf4fe2eSAlexander Motin pm_ports = 5; 31613014ca0SSøren Schmidt break; 31713014ca0SSøren Schmidt 31813014ca0SSøren Schmidt default: 3199cf4fe2eSAlexander Motin device_printf(dev, "Port Multiplier (id=%08x rev=%x) with %d ports\n", 32013014ca0SSøren Schmidt pm_chipid, pm_revision, pm_ports); 32113014ca0SSøren Schmidt } 32213014ca0SSøren Schmidt 32313014ca0SSøren Schmidt /* reset all ports and register if anything connected */ 32413014ca0SSøren Schmidt for (port=0; port < pm_ports; port++) { 3259cf4fe2eSAlexander Motin u_int32_t signature; 32613014ca0SSøren Schmidt 3279cf4fe2eSAlexander Motin if (!ata_sata_phy_reset(dev, port, 1)) 32813014ca0SSøren Schmidt continue; 32913014ca0SSøren Schmidt 3309cf4fe2eSAlexander Motin /* 3319cf4fe2eSAlexander Motin * XXX: I have no idea how to properly wait for PMP port hardreset 3329cf4fe2eSAlexander Motin * completion. Without this delay soft reset does not completes 3339cf4fe2eSAlexander Motin * successfully. 3349cf4fe2eSAlexander Motin */ 3359cf4fe2eSAlexander Motin DELAY(1000000); 33613014ca0SSøren Schmidt 33713014ca0SSøren Schmidt signature = ch->hw.softreset(dev, port); 33813014ca0SSøren Schmidt 33913014ca0SSøren Schmidt if (bootverbose) 34013014ca0SSøren Schmidt device_printf(dev, "p%d: SIGNATURE=%08x\n", port, signature); 34113014ca0SSøren Schmidt 34213014ca0SSøren Schmidt /* figure out whats there */ 343e412a8c3SAlexander Motin switch (signature >> 16) { 344e412a8c3SAlexander Motin case 0x0000: 34513014ca0SSøren Schmidt ch->devices |= (ATA_ATA_MASTER << port); 34613014ca0SSøren Schmidt continue; 347e412a8c3SAlexander Motin case 0xeb14: 34813014ca0SSøren Schmidt ch->devices |= (ATA_ATAPI_MASTER << port); 34913014ca0SSøren Schmidt continue; 35013014ca0SSøren Schmidt } 35113014ca0SSøren Schmidt } 35213014ca0SSøren Schmidt } 353