| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * IDE host driver for AT91 (SAM9, CAP9, AT572D940HF) Static Memory Controller | 
|  | 3 | * with Compact Flash True IDE logic | 
|  | 4 | * | 
|  | 5 | * Copyright (c) 2008, 2009 Kelvatek Ltd. | 
|  | 6 | * | 
|  | 7 | *  This program is free software; you can redistribute it and/or modify | 
|  | 8 | * it under the terms of the GNU General Public License as published by | 
|  | 9 | * the Free Software Foundation; either version 2 of the License, or | 
|  | 10 | * (at your option) any later version. | 
|  | 11 | * | 
|  | 12 | * This program is distributed in the hope that it will be useful, | 
|  | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 15 | * GNU General Public License for more details. | 
|  | 16 | * | 
|  | 17 | * You should have received a copy of the GNU General Public License | 
|  | 18 | * along with this program; if not, write to the Free Software | 
|  | 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | 20 | * | 
|  | 21 | */ | 
|  | 22 |  | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 23 | #include <linux/kernel.h> | 
|  | 24 | #include <linux/module.h> | 
|  | 25 | #include <linux/clk.h> | 
|  | 26 | #include <linux/err.h> | 
|  | 27 | #include <linux/ide.h> | 
|  | 28 | #include <linux/platform_device.h> | 
|  | 29 |  | 
|  | 30 | #include <mach/board.h> | 
|  | 31 | #include <mach/gpio.h> | 
|  | 32 | #include <mach/at91sam9263.h> | 
|  | 33 | #include <mach/at91sam9_smc.h> | 
|  | 34 | #include <mach/at91sam9263_matrix.h> | 
|  | 35 |  | 
|  | 36 | #define DRV_NAME "at91_ide" | 
|  | 37 |  | 
|  | 38 | #define perr(fmt, args...) pr_err(DRV_NAME ": " fmt, ##args) | 
|  | 39 | #define pdbg(fmt, args...) pr_debug("%s " fmt, __func__, ##args) | 
|  | 40 |  | 
|  | 41 | /* | 
|  | 42 | * Access to IDE device is possible through EBI Static Memory Controller | 
|  | 43 | * with Compact Flash logic. For details see EBI and SMC datasheet sections | 
|  | 44 | * of any microcontroller from AT91SAM9 family. | 
|  | 45 | * | 
|  | 46 | * Within SMC chip select address space, lines A[23:21] distinguish Compact | 
|  | 47 | * Flash modes (I/O, common memory, attribute memory, True IDE). IDE modes are: | 
|  | 48 | *   0x00c0000 - True IDE | 
|  | 49 | *   0x00e0000 - Alternate True IDE (Alt Status Register) | 
|  | 50 | * | 
|  | 51 | * On True IDE mode Task File and Data Register are mapped at the same address. | 
|  | 52 | * To distinguish access between these two different bus data width is used: | 
|  | 53 | * 8Bit for Task File, 16Bit for Data I/O. | 
|  | 54 | * | 
|  | 55 | * After initialization we do 8/16 bit flipping (changes in SMC MODE register) | 
|  | 56 | * only inside IDE callback routines which are serialized by IDE layer, | 
|  | 57 | * so no additional locking needed. | 
|  | 58 | */ | 
|  | 59 |  | 
|  | 60 | #define TASK_FILE	0x00c00000 | 
|  | 61 | #define ALT_MODE	0x00e00000 | 
|  | 62 | #define REGS_SIZE	8 | 
|  | 63 |  | 
|  | 64 | #define enter_16bit(cs, mode) do {					\ | 
|  | 65 | mode = at91_sys_read(AT91_SMC_MODE(cs));			\ | 
|  | 66 | at91_sys_write(AT91_SMC_MODE(cs), mode | AT91_SMC_DBW_16);	\ | 
|  | 67 | } while (0) | 
|  | 68 |  | 
|  | 69 | #define leave_16bit(cs, mode) at91_sys_write(AT91_SMC_MODE(cs), mode); | 
|  | 70 |  | 
|  | 71 | static void set_smc_timings(const u8 chipselect, const u16 cycle, | 
|  | 72 | const u16 setup, const u16 pulse, | 
|  | 73 | const u16 data_float, int use_iordy) | 
|  | 74 | { | 
|  | 75 | unsigned long mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | | 
|  | 76 | AT91_SMC_BAT_SELECT; | 
|  | 77 |  | 
|  | 78 | /* disable or enable waiting for IORDY signal */ | 
|  | 79 | if (use_iordy) | 
|  | 80 | mode |= AT91_SMC_EXNWMODE_READY; | 
|  | 81 |  | 
|  | 82 | /* add data float cycles if needed */ | 
|  | 83 | if (data_float) | 
|  | 84 | mode |= AT91_SMC_TDF_(data_float); | 
|  | 85 |  | 
|  | 86 | at91_sys_write(AT91_SMC_MODE(chipselect), mode); | 
|  | 87 |  | 
|  | 88 | /* setup timings in SMC */ | 
|  | 89 | at91_sys_write(AT91_SMC_SETUP(chipselect), AT91_SMC_NWESETUP_(setup) | | 
|  | 90 | AT91_SMC_NCS_WRSETUP_(0) | | 
|  | 91 | AT91_SMC_NRDSETUP_(setup) | | 
|  | 92 | AT91_SMC_NCS_RDSETUP_(0)); | 
|  | 93 | at91_sys_write(AT91_SMC_PULSE(chipselect), AT91_SMC_NWEPULSE_(pulse) | | 
|  | 94 | AT91_SMC_NCS_WRPULSE_(cycle) | | 
|  | 95 | AT91_SMC_NRDPULSE_(pulse) | | 
|  | 96 | AT91_SMC_NCS_RDPULSE_(cycle)); | 
|  | 97 | at91_sys_write(AT91_SMC_CYCLE(chipselect), AT91_SMC_NWECYCLE_(cycle) | | 
|  | 98 | AT91_SMC_NRDCYCLE_(cycle)); | 
|  | 99 | } | 
|  | 100 |  | 
|  | 101 | static unsigned int calc_mck_cycles(unsigned int ns, unsigned int mck_hz) | 
|  | 102 | { | 
|  | 103 | u64 tmp = ns; | 
|  | 104 |  | 
|  | 105 | tmp *= mck_hz; | 
|  | 106 | tmp += 1000*1000*1000 - 1; /* round up */ | 
|  | 107 | do_div(tmp, 1000*1000*1000); | 
|  | 108 | return (unsigned int) tmp; | 
|  | 109 | } | 
|  | 110 |  | 
|  | 111 | static void apply_timings(const u8 chipselect, const u8 pio, | 
|  | 112 | const struct ide_timing *timing, int use_iordy) | 
|  | 113 | { | 
|  | 114 | unsigned int t0, t1, t2, t6z; | 
|  | 115 | unsigned int cycle, setup, pulse, data_float; | 
|  | 116 | unsigned int mck_hz; | 
|  | 117 | struct clk *mck; | 
|  | 118 |  | 
|  | 119 | /* see table 22 of Compact Flash standard 4.1 for the meaning, | 
|  | 120 | * we do not stretch active (t2) time, so setup (t1) + hold time (th) | 
|  | 121 | * assure at least minimal recovery (t2i) time */ | 
|  | 122 | t0 = timing->cyc8b; | 
|  | 123 | t1 = timing->setup; | 
|  | 124 | t2 = timing->act8b; | 
|  | 125 | t6z = (pio < 5) ? 30 : 20; | 
|  | 126 |  | 
|  | 127 | pdbg("t0=%u t1=%u t2=%u t6z=%u\n", t0, t1, t2, t6z); | 
|  | 128 |  | 
|  | 129 | mck = clk_get(NULL, "mck"); | 
|  | 130 | BUG_ON(IS_ERR(mck)); | 
|  | 131 | mck_hz = clk_get_rate(mck); | 
|  | 132 | pdbg("mck_hz=%u\n", mck_hz); | 
|  | 133 |  | 
|  | 134 | cycle = calc_mck_cycles(t0, mck_hz); | 
|  | 135 | setup = calc_mck_cycles(t1, mck_hz); | 
|  | 136 | pulse = calc_mck_cycles(t2, mck_hz); | 
|  | 137 | data_float = calc_mck_cycles(t6z, mck_hz); | 
|  | 138 |  | 
|  | 139 | pdbg("cycle=%u setup=%u pulse=%u data_float=%u\n", | 
|  | 140 | cycle, setup, pulse, data_float); | 
|  | 141 |  | 
|  | 142 | set_smc_timings(chipselect, cycle, setup, pulse, data_float, use_iordy); | 
|  | 143 | } | 
|  | 144 |  | 
| Bartlomiej Zolnierkiewicz | adb1af9 | 2009-03-27 12:46:38 +0100 | [diff] [blame] | 145 | static void at91_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 146 | void *buf, unsigned int len) | 
|  | 147 | { | 
|  | 148 | ide_hwif_t *hwif = drive->hwif; | 
|  | 149 | struct ide_io_ports *io_ports = &hwif->io_ports; | 
|  | 150 | u8 chipselect = hwif->select_data; | 
|  | 151 | unsigned long mode; | 
|  | 152 |  | 
|  | 153 | pdbg("cs %u buf %p len %d\n", chipselect, buf, len); | 
|  | 154 |  | 
|  | 155 | len++; | 
|  | 156 |  | 
|  | 157 | enter_16bit(chipselect, mode); | 
| Bartlomiej Zolnierkiewicz | 443d18c | 2009-03-27 12:46:24 +0100 | [diff] [blame] | 158 | readsw((void __iomem *)io_ports->data_addr, buf, len / 2); | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 159 | leave_16bit(chipselect, mode); | 
|  | 160 | } | 
|  | 161 |  | 
| Bartlomiej Zolnierkiewicz | adb1af9 | 2009-03-27 12:46:38 +0100 | [diff] [blame] | 162 | static void at91_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 163 | void *buf, unsigned int len) | 
|  | 164 | { | 
|  | 165 | ide_hwif_t *hwif = drive->hwif; | 
|  | 166 | struct ide_io_ports *io_ports = &hwif->io_ports; | 
|  | 167 | u8 chipselect = hwif->select_data; | 
|  | 168 | unsigned long mode; | 
|  | 169 |  | 
|  | 170 | pdbg("cs %u buf %p len %d\n", chipselect,  buf, len); | 
|  | 171 |  | 
|  | 172 | enter_16bit(chipselect, mode); | 
| Bartlomiej Zolnierkiewicz | 443d18c | 2009-03-27 12:46:24 +0100 | [diff] [blame] | 173 | writesw((void __iomem *)io_ports->data_addr, buf, len / 2); | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 174 | leave_16bit(chipselect, mode); | 
|  | 175 | } | 
|  | 176 |  | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 177 | static void at91_ide_set_pio_mode(ide_drive_t *drive, const u8 pio) | 
|  | 178 | { | 
|  | 179 | struct ide_timing *timing; | 
|  | 180 | u8 chipselect = drive->hwif->select_data; | 
|  | 181 | int use_iordy = 0; | 
|  | 182 |  | 
|  | 183 | pdbg("chipselect %u pio %u\n", chipselect, pio); | 
|  | 184 |  | 
|  | 185 | timing = ide_timing_find_mode(XFER_PIO_0 + pio); | 
|  | 186 | BUG_ON(!timing); | 
|  | 187 |  | 
| Bartlomiej Zolnierkiewicz | c9ef59f | 2009-06-15 18:52:53 +0200 | [diff] [blame] | 188 | if (ide_pio_need_iordy(drive, pio)) | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 189 | use_iordy = 1; | 
|  | 190 |  | 
|  | 191 | apply_timings(chipselect, pio, timing, use_iordy); | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | static const struct ide_tp_ops at91_ide_tp_ops = { | 
|  | 195 | .exec_command	= ide_exec_command, | 
|  | 196 | .read_status	= ide_read_status, | 
|  | 197 | .read_altstatus	= ide_read_altstatus, | 
| Sergei Shtylyov | ecf3a31 | 2009-03-31 20:15:30 +0200 | [diff] [blame] | 198 | .write_devctl	= ide_write_devctl, | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 199 |  | 
| Sergei Shtylyov | abb596b | 2009-03-31 20:15:32 +0200 | [diff] [blame] | 200 | .dev_select	= ide_dev_select, | 
| Sergei Shtylyov | 7636e45 | 2009-04-08 14:12:51 +0200 | [diff] [blame] | 201 | .tf_load	= ide_tf_load, | 
|  | 202 | .tf_read	= ide_tf_read, | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 203 |  | 
|  | 204 | .input_data	= at91_ide_input_data, | 
|  | 205 | .output_data	= at91_ide_output_data, | 
|  | 206 | }; | 
|  | 207 |  | 
|  | 208 | static const struct ide_port_ops at91_ide_port_ops = { | 
|  | 209 | .set_pio_mode	= at91_ide_set_pio_mode, | 
|  | 210 | }; | 
|  | 211 |  | 
|  | 212 | static const struct ide_port_info at91_ide_port_info __initdata = { | 
|  | 213 | .port_ops	= &at91_ide_port_ops, | 
|  | 214 | .tp_ops		= &at91_ide_tp_ops, | 
|  | 215 | .host_flags 	= IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA | IDE_HFLAG_SINGLE | | 
|  | 216 | IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_UNMASK_IRQS, | 
| Stanislaw Gruszka | fb4252e | 2009-04-08 14:13:04 +0200 | [diff] [blame] | 217 | .pio_mask 	= ATA_PIO6, | 
| Bartlomiej Zolnierkiewicz | 29e52cf | 2009-05-17 19:12:22 +0200 | [diff] [blame] | 218 | .chipset	= ide_generic, | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 219 | }; | 
|  | 220 |  | 
|  | 221 | /* | 
|  | 222 | * If interrupt is delivered through GPIO, IRQ are triggered on falling | 
|  | 223 | * and rising edge of signal. Whereas IDE device request interrupt on high | 
|  | 224 | * level (rising edge in our case). This mean we have fake interrupts, so | 
|  | 225 | * we need to check interrupt pin and exit instantly from ISR when line | 
|  | 226 | * is on low level. | 
|  | 227 | */ | 
|  | 228 |  | 
|  | 229 | irqreturn_t at91_irq_handler(int irq, void *dev_id) | 
|  | 230 | { | 
|  | 231 | int ntries = 8; | 
|  | 232 | int pin_val1, pin_val2; | 
|  | 233 |  | 
|  | 234 | /* additional deglitch, line can be noisy in badly designed PCB */ | 
|  | 235 | do { | 
|  | 236 | pin_val1 = at91_get_gpio_value(irq); | 
|  | 237 | pin_val2 = at91_get_gpio_value(irq); | 
|  | 238 | } while (pin_val1 != pin_val2 && --ntries > 0); | 
|  | 239 |  | 
|  | 240 | if (pin_val1 == 0 || ntries <= 0) | 
|  | 241 | return IRQ_HANDLED; | 
|  | 242 |  | 
|  | 243 | return ide_intr(irq, dev_id); | 
|  | 244 | } | 
|  | 245 |  | 
|  | 246 | static int __init at91_ide_probe(struct platform_device *pdev) | 
|  | 247 | { | 
|  | 248 | int ret; | 
| Bartlomiej Zolnierkiewicz | 9f36d31 | 2009-05-17 19:12:25 +0200 | [diff] [blame] | 249 | struct ide_hw hw, *hws[] = { &hw }; | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 250 | struct ide_host *host; | 
|  | 251 | struct resource *res; | 
|  | 252 | unsigned long tf_base = 0, ctl_base = 0; | 
|  | 253 | struct at91_cf_data *board = pdev->dev.platform_data; | 
|  | 254 |  | 
|  | 255 | if (!board) | 
|  | 256 | return -ENODEV; | 
|  | 257 |  | 
|  | 258 | if (board->det_pin && at91_get_gpio_value(board->det_pin) != 0) { | 
|  | 259 | perr("no device detected\n"); | 
|  | 260 | return -ENODEV; | 
|  | 261 | } | 
|  | 262 |  | 
|  | 263 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 264 | if (!res) { | 
|  | 265 | perr("can't get memory resource\n"); | 
|  | 266 | return -ENODEV; | 
|  | 267 | } | 
|  | 268 |  | 
|  | 269 | if (!devm_request_mem_region(&pdev->dev, res->start + TASK_FILE, | 
|  | 270 | REGS_SIZE, "ide") || | 
|  | 271 | !devm_request_mem_region(&pdev->dev, res->start + ALT_MODE, | 
|  | 272 | REGS_SIZE, "alt")) { | 
|  | 273 | perr("memory resources in use\n"); | 
|  | 274 | return -EBUSY; | 
|  | 275 | } | 
|  | 276 |  | 
|  | 277 | pdbg("chipselect %u irq %u res %08lx\n", board->chipselect, | 
|  | 278 | board->irq_pin, (unsigned long) res->start); | 
|  | 279 |  | 
|  | 280 | tf_base = (unsigned long) devm_ioremap(&pdev->dev, res->start + TASK_FILE, | 
|  | 281 | REGS_SIZE); | 
|  | 282 | ctl_base = (unsigned long) devm_ioremap(&pdev->dev, res->start + ALT_MODE, | 
|  | 283 | REGS_SIZE); | 
|  | 284 | if (!tf_base || !ctl_base) { | 
|  | 285 | perr("can't map memory regions\n"); | 
|  | 286 | return -EBUSY; | 
|  | 287 | } | 
|  | 288 |  | 
|  | 289 | memset(&hw, 0, sizeof(hw)); | 
|  | 290 |  | 
|  | 291 | if (board->flags & AT91_IDE_SWAP_A0_A2) { | 
|  | 292 | /* workaround for stupid hardware bug */ | 
|  | 293 | hw.io_ports.data_addr	= tf_base + 0; | 
|  | 294 | hw.io_ports.error_addr	= tf_base + 4; | 
|  | 295 | hw.io_ports.nsect_addr	= tf_base + 2; | 
|  | 296 | hw.io_ports.lbal_addr	= tf_base + 6; | 
|  | 297 | hw.io_ports.lbam_addr	= tf_base + 1; | 
|  | 298 | hw.io_ports.lbah_addr	= tf_base + 5; | 
|  | 299 | hw.io_ports.device_addr = tf_base + 3; | 
|  | 300 | hw.io_ports.command_addr = tf_base + 7; | 
|  | 301 | hw.io_ports.ctl_addr	= ctl_base + 3; | 
|  | 302 | } else | 
|  | 303 | ide_std_init_ports(&hw, tf_base, ctl_base + 6); | 
|  | 304 |  | 
|  | 305 | hw.irq = board->irq_pin; | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 306 | hw.dev = &pdev->dev; | 
|  | 307 |  | 
| Bartlomiej Zolnierkiewicz | dca3983 | 2009-05-17 19:12:24 +0200 | [diff] [blame] | 308 | host = ide_host_alloc(&at91_ide_port_info, hws, 1); | 
| Stanislaw Gruszka | 6e5f1e1 | 2009-03-05 16:10:58 +0100 | [diff] [blame] | 309 | if (!host) { | 
|  | 310 | perr("failed to allocate ide host\n"); | 
|  | 311 | return -ENOMEM; | 
|  | 312 | } | 
|  | 313 |  | 
|  | 314 | /* setup Static Memory Controller - PIO 0 as default */ | 
|  | 315 | apply_timings(board->chipselect, 0, ide_timing_find_mode(XFER_PIO_0), 0); | 
|  | 316 |  | 
|  | 317 | /* with GPIO interrupt we have to do quirks in handler */ | 
|  | 318 | if (board->irq_pin >= PIN_BASE) | 
|  | 319 | host->irq_handler = at91_irq_handler; | 
|  | 320 |  | 
|  | 321 | host->ports[0]->select_data = board->chipselect; | 
|  | 322 |  | 
|  | 323 | ret = ide_host_register(host, &at91_ide_port_info, hws); | 
|  | 324 | if (ret) { | 
|  | 325 | perr("failed to register ide host\n"); | 
|  | 326 | goto err_free_host; | 
|  | 327 | } | 
|  | 328 | platform_set_drvdata(pdev, host); | 
|  | 329 | return 0; | 
|  | 330 |  | 
|  | 331 | err_free_host: | 
|  | 332 | ide_host_free(host); | 
|  | 333 | return ret; | 
|  | 334 | } | 
|  | 335 |  | 
|  | 336 | static int __exit at91_ide_remove(struct platform_device *pdev) | 
|  | 337 | { | 
|  | 338 | struct ide_host *host = platform_get_drvdata(pdev); | 
|  | 339 |  | 
|  | 340 | ide_host_remove(host); | 
|  | 341 | return 0; | 
|  | 342 | } | 
|  | 343 |  | 
|  | 344 | static struct platform_driver at91_ide_driver = { | 
|  | 345 | .driver	= { | 
|  | 346 | .name = DRV_NAME, | 
|  | 347 | .owner = THIS_MODULE, | 
|  | 348 | }, | 
|  | 349 | .remove	= __exit_p(at91_ide_remove), | 
|  | 350 | }; | 
|  | 351 |  | 
|  | 352 | static int __init at91_ide_init(void) | 
|  | 353 | { | 
|  | 354 | return platform_driver_probe(&at91_ide_driver, at91_ide_probe); | 
|  | 355 | } | 
|  | 356 |  | 
|  | 357 | static void __exit at91_ide_exit(void) | 
|  | 358 | { | 
|  | 359 | platform_driver_unregister(&at91_ide_driver); | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | module_init(at91_ide_init); | 
|  | 363 | module_exit(at91_ide_exit); | 
|  | 364 |  | 
|  | 365 | MODULE_LICENSE("GPL"); | 
|  | 366 | MODULE_AUTHOR("Stanislaw Gruszka <stf_xl@wp.pl>"); | 
|  | 367 |  |