Add WIP flash driver for AT45DBxx1
Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/1d0b37b5 Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/1d0b37b5 Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/1d0b37b5 Branch: refs/heads/sensors_branch Commit: 1d0b37b59345c331e0e376ee14ba71e613d8b4b1 Parents: 13cc780 Author: Fabio Utzig <ut...@utzig.org> Authored: Mon Jan 2 14:39:56 2017 -0200 Committer: Fabio Utzig <ut...@utzig.org> Committed: Fri Jan 20 07:27:39 2017 -0200 ---------------------------------------------------------------------- hw/drivers/flash/include/flash/flash.h | 45 ++++ hw/drivers/flash/pkg.yml | 27 ++ hw/drivers/flash/src/flash.c | 375 ++++++++++++++++++++++++++++ 3 files changed, 447 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/1d0b37b5/hw/drivers/flash/include/flash/flash.h ---------------------------------------------------------------------- diff --git a/hw/drivers/flash/include/flash/flash.h b/hw/drivers/flash/include/flash/flash.h new file mode 100644 index 0000000..c0807ff --- /dev/null +++ b/hw/drivers/flash/include/flash/flash.h @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __FLASH_H__ +#define __FLASH_H__ + +#include <os/os_dev.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int +flash_init(int spi_num, void *spi_cfg, int ss_pin, uint32_t baudrate); + +int +flash_read(uint8_t flash_id, uint32_t addr, void *buf, size_t len); + +int +flash_write(uint8_t flash_id, uint32_t addr, const void *buf, size_t len); + +int +flash_erase(uint8_t flash_id, uint32_t addr, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* __MMC_H__ */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/1d0b37b5/hw/drivers/flash/pkg.yml ---------------------------------------------------------------------- diff --git a/hw/drivers/flash/pkg.yml b/hw/drivers/flash/pkg.yml new file mode 100644 index 0000000..cb37e01 --- /dev/null +++ b/hw/drivers/flash/pkg.yml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: hw/drivers/flash +pkg.description: Flash memories +pkg.author: "Apache Mynewt <d...@mynewt.incubator.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - hw/hal http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/1d0b37b5/hw/drivers/flash/src/flash.c ---------------------------------------------------------------------- diff --git a/hw/drivers/flash/src/flash.c b/hw/drivers/flash/src/flash.c new file mode 100644 index 0000000..b5b7caf --- /dev/null +++ b/hw/drivers/flash/src/flash.c @@ -0,0 +1,375 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <hal/hal_spi.h> +#include <hal/hal_gpio.h> + +#include <flash/flash.h> + +#include <stdio.h> + +/** + * Memory Architecture: + * + * Device can be addressed using pages, blocks or sectors. + * + * 1) Page + * - device has 8192 pages of 512 (or 528) bytes. + * + * 2) Block + * - device has 1024 blocks of 4K (or 4K + 128) bytes. + * - Each block contains 8 pages, eg, Block 0 == Page 0 to 7, etc. + * + * 3) Sector + * - Sector 0 == Block 0. + * - Sector 1 == Blocks 1 to 63 (252K + 8064). + * - Sector 2 to 16 contain 64 blocks (256K + 8192). + */ + +#define MEM_READ 0x52 /* Read page bypassing buffer */ +#define BUF1_READ 0x54 +#define BUF2_READ 0x56 +#define MEM_TO_BUF1_TRANSFER 0x53 +#define MEM_TO_BUF2_TRANSFER 0x55 +#define MEM_TO_BUF1_CMP 0x60 +#define MEM_TO_BUF2_CMP 0x61 +#define BUF1_WRITE 0x84 +#define BUF2_WRITE 0x87 +#define BUF1_TO_MEM_ERASE 0x83 +#define BUF2_TO_MEM_ERASE 0x86 +#define BUF1_TO_MEM_NO_ERASE 0x88 +#define BUF2_TO_MEM_NO_ERASE 0x89 +#define PAGE_ERASE 0x81 +#define BLOCK_ERASE 0x50 + + +#define PAGE_ERASE 0x81 +#define BLOCK_ERASE 0x50 + +#define STATUS_REGISTER 0x57 + +#define STATUS_BUSY (1 << 7) +#define STATUS_CMP (1 << 6) + +#define MIN(n, m) (((n) < (m)) ? (n) : (m)) + +#define PAGE_SIZE 512 +#define PAGE_BITS 9 + +/** + * Reading memory (MEM_READ): + * < r, PA12-6 > + * < PA5-0, BA9-8 > + * < BA7-0 > + * < 8 don't care bits > + * < 8 don't care bits > + * < 8 don't care bits > + * < 8 don't care bits > + * + * Reading a buffer (BUFx_READ): + * < 8 don't care bits > + * < 6 don't care bits, A9-8 > + * < A7-0 > + * < 8 don't care bits > + * + * Memory to buffer copy (MEM_TO_BUFx_TRANSFER): + * < r, PA12-PA6 > + * < PA5-0, 2 don't care bits > + * < 8 don't care bits > + * + * Memory to buffer compare (MEM_TO_BUFx_CMP): + * < r, PA12-PA6 > + * < PA5-0, 2 don't care bits > + * < 8 don't care bits > + * + * Buffer write (BUFx_WRITE): + * < 8 don't care bits > + * < 6 don't care bits, BFA9-8 > + * < BFA7-0 > + * + * Buffer to memory program with erase (BUFx_TO_MEM_ERASE): + * < r, PA12-PA6 > + * < PA5-0, 2 don't care bits > + * < 8 don't care bits > + * + * Buffer to memory program without erase (BUFx_TO_MEM_NO_ERASE): + * < r, PA12-PA6 > + * < PA5-0, 2 don't care bits > + * < 8 don't care bits > + * + * Page erase (PAGE_ERASE): + * < r, PA12-PA6 > + * < PA5-0, 2 don't care bits > + * < 8 don't care bits > + * + * Block erase (BLOCK_ERASE): + * < r, PA12-PA6 > + * < PA5-PA3, 5 don't care bits > + * < 8 don't care bits > + */ + +static struct flash_dev { + int spi_num; + int ss_pin; + void *spi_cfg; + struct hal_spi_settings *settings; +} g_flash_dev; + +static struct hal_spi_settings at45db_settings = { + .data_order = HAL_SPI_MSB_FIRST, + .data_mode = HAL_SPI_MODE3, + .baudrate = 100, /* NOTE: default clock to be overwritten by init */ + .word_size = HAL_SPI_WORD_SIZE_8BIT, +}; + +static struct flash_dev * +cfg_dev(uint8_t id) +{ + if (id != 0) { + return NULL; + } + + return &g_flash_dev; +} + +static uint8_t read_status(struct flash_dev *flash) +{ + uint8_t val; + + hal_gpio_write(flash->ss_pin, 0); + + hal_spi_tx_val(flash->spi_num, STATUS_REGISTER); + val = hal_spi_tx_val(flash->spi_num, 0xff); + + hal_gpio_write(flash->ss_pin, 1); + + //printf("STATUS=[%02x]\n", val); + + return val; +} + +static inline bool +device_ready(struct flash_dev *dev) +{ + return ((read_status(dev) & STATUS_BUSY) != 0); +} + +static inline bool +buffer_equal(struct flash_dev *dev) +{ + return ((read_status(dev) & STATUS_CMP) == 0); +} + +static inline uint16_t +page_address(uint32_t addr) +{ + return (uint16_t) addr / PAGE_SIZE; +} + +static inline uint16_t +block_address(uint32_t addr) +{ + return (uint16_t) addr % PAGE_SIZE; +} + +/* FIXME: add timeout */ +static inline void +wait_ready(struct flash_dev *dev) +{ + while (!device_ready(dev)) { + os_time_delay(OS_TICKS_PER_SEC / 10000); + } +} + +int +flash_init(int spi_num, void *spi_cfg, int ss_pin, uint32_t baudrate) +{ + int rc; + struct flash_dev *dev; + + at45db_settings.baudrate = baudrate; + + dev = &g_flash_dev; + dev->spi_num = spi_num; + dev->ss_pin = ss_pin; + dev->spi_cfg = spi_cfg; + dev->settings = &at45db_settings; + + hal_gpio_init_out(dev->ss_pin, 1); + + rc = hal_spi_init(dev->spi_num, dev->spi_cfg, HAL_SPI_TYPE_MASTER); + if (rc) { + return (rc); + } + + rc = hal_spi_config(dev->spi_num, dev->settings); + if (rc) { + return (rc); + } + + hal_spi_set_txrx_cb(dev->spi_num, NULL, NULL); + hal_spi_enable(dev->spi_num); + + return 0; +} + +int +flash_read(uint8_t flash_id, uint32_t addr, void *buf, size_t len) +{ + struct flash_dev *dev; + uint16_t pa; + uint16_t ba; + uint8_t val; + uint32_t n; + uint16_t page_count; + uint16_t offset; + + dev = cfg_dev(flash_id); + if (!dev) { + return -1; + } + + /* FIXME: when reading beyond the end of the page, it rolls back to the + * beginning of the same page... + */ + + page_count = (len / (PAGE_SIZE + 1)) + 1; + offset = 0; + + while (page_count--) { + wait_ready(dev); + + hal_gpio_write(dev->ss_pin, 0); + + hal_spi_tx_val(dev->spi_num, MEM_READ); + + pa = page_address(addr); + ba = block_address(addr); + + /* FIXME: not using BA9 (required to access more than 512 bytes on page) */ + + /* < r, PA12-6 > */ + val = (pa >> 6) & ~0x80; + hal_spi_tx_val(dev->spi_num, val); + + /* < PA5-0, BA9-8 > */ + val = (pa << 2) | ((ba >> 8) & 1); + hal_spi_tx_val(dev->spi_num, val); + + /* < BA7-0 > */ + hal_spi_tx_val(dev->spi_num, ba); + + /* send 32 don't care bits */ + hal_spi_tx_val(dev->spi_num, 0xff); + hal_spi_tx_val(dev->spi_num, 0xff); + hal_spi_tx_val(dev->spi_num, 0xff); + hal_spi_tx_val(dev->spi_num, 0xff); + + for (n = 0; n < len; n++) { + *((uint8_t *) buf + n) = hal_spi_tx_val(dev->spi_num, 0xff); + } + + hal_gpio_write(dev->ss_pin, 1); + + offset += PAGE_SIZE; + } + + return 0; +} + +int +flash_write(uint8_t flash_id, uint32_t addr, const void *buf, size_t len) +{ + struct flash_dev *dev; + uint16_t pa; + //uint16_t bfa; + //uint8_t val; + uint32_t n; + int page_count; + int offset; + + dev = cfg_dev(flash_id); + if (!dev) { + return -1; + } + + /* FIXME: calculation is assuming that addr starts at offset 0 in page */ + page_count = (len / (PAGE_SIZE + 1)) + 1; + + if (!page_count) { + return 0; + } + + /* FIXME: when reading beyond the end of the page, it rolls back to the + * beginning of the same page... + */ + + offset = 0; + while (page_count-- >= 0) { + wait_ready(dev); + + hal_gpio_write(dev->ss_pin, 0); + + /* FIXME: ping-pong between page 1 and 2 */ + hal_spi_tx_val(dev->spi_num, BUF1_WRITE); + + /* FIXME: always writing at 0 on buffer */ + hal_spi_tx_val(dev->spi_num, 0xff); + hal_spi_tx_val(dev->spi_num, 0xfc); + hal_spi_tx_val(dev->spi_num, 0); + + for (n = 0; n < len; n++) { + hal_spi_tx_val(dev->spi_num, *((uint8_t *) buf + n)); + } + + hal_gpio_write(dev->ss_pin, 1); + + wait_ready(dev); + + hal_gpio_write(dev->ss_pin, 0); + + hal_spi_tx_val(dev->spi_num, BUF1_TO_MEM_ERASE); + + /* FIXME: check that pa doesn't overflow capacity */ + pa = addr / PAGE_SIZE; + + hal_spi_tx_val(dev->spi_num, (pa >> 6) & ~0x80); + hal_spi_tx_val(dev->spi_num, (pa << 2) | 0x3); + hal_spi_tx_val(dev->spi_num, 0xff); + + hal_gpio_write(dev->ss_pin, 1); + + offset += PAGE_SIZE; + } + + return 0; +} + +int +flash_erase(uint8_t flash_id, uint32_t addr, size_t len) +{ + struct flash_dev *dev; + + dev = cfg_dev(flash_id); + if (!dev) { + return -1; + } + + return 0; +}