123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- /*
- * wcxb SPI library
- *
- * Copyright (C) 2013 Digium, Inc.
- *
- * All rights reserved.
- *
- */
- /*
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2 as published by the
- * Free Software Foundation. See the LICENSE file included with
- * this program for more details.
- */
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/device.h>
- #include <linux/sched.h>
- #include "wcxb_spi.h"
- #include "wcxb_flash.h"
- #define FLASH_PAGE_PROGRAM 0x02
- #define FLASH_READ 0x03
- #define FLASH_READ_STATUS 0x05
- #define FLASH_WRITE_ENABLE 0x06
- #define FLASH_SECTOR_ERASE 0xd8
- static int wcxb_flash_read_status_register(struct wcxb_spi_device *spi,
- u8 *status)
- {
- u8 command[] = {
- FLASH_READ_STATUS,
- };
- struct wcxb_spi_transfer t_cmd = {
- .tx_buf = command,
- .len = sizeof(command),
- };
- struct wcxb_spi_transfer t_serial = {
- .rx_buf = status,
- .len = 1,
- };
- struct wcxb_spi_message m;
- wcxb_spi_message_init(&m);
- wcxb_spi_message_add_tail(&t_cmd, &m);
- wcxb_spi_message_add_tail(&t_serial, &m);
- return wcxb_spi_sync(spi, &m);
- }
- int wcxb_flash_read(struct wcxb_spi_device *spi, unsigned int address,
- void *data, size_t len)
- {
- u8 command[] = {
- FLASH_READ,
- (address & 0xff0000) >> 16,
- (address & 0xff00) >> 8,
- (address & 0xff)
- };
- struct wcxb_spi_transfer t_cmd = {
- .tx_buf = command,
- .len = sizeof(command),
- };
- struct wcxb_spi_transfer t_serial = {
- .rx_buf = data,
- .len = len,
- };
- struct wcxb_spi_message m;
- wcxb_spi_message_init(&m);
- wcxb_spi_message_add_tail(&t_cmd, &m);
- wcxb_spi_message_add_tail(&t_serial, &m);
- return wcxb_spi_sync(spi, &m);
- }
- static int wcxb_flash_wait_until_not_busy(struct wcxb_spi_device *spi)
- {
- int res;
- u8 status;
- unsigned long stop = jiffies + 5*HZ;
- do {
- res = wcxb_flash_read_status_register(spi, &status);
- } while (!res && (status & 0x1) && time_before(jiffies, stop));
- if (!res)
- return res;
- if (time_after_eq(jiffies, stop))
- return -EIO;
- return 0;
- }
- static int wcxb_flash_write_enable(struct wcxb_spi_device *spi)
- {
- u8 command = FLASH_WRITE_ENABLE;
- return wcxb_spi_write(spi, &command, 1);
- }
- int wcxb_flash_sector_erase(struct wcxb_spi_device *spi,
- unsigned int address)
- {
- int res;
- u8 command[] = {FLASH_SECTOR_ERASE, (address >> 16)&0xff, 0x00, 0x00};
- /* Sector must be on 64KB boundary. */
- if (address & 0xffff)
- return -EINVAL;
- /* Start the erase. */
- res = wcxb_flash_write_enable(spi);
- if (res)
- return res;
- res = wcxb_spi_write(spi, &command, sizeof(command));
- if (res)
- return res;
- return wcxb_flash_wait_until_not_busy(spi);
- }
- int wcxb_flash_write(struct wcxb_spi_device *spi, unsigned int address,
- const void *data, size_t len)
- {
- int res;
- const size_t FLASH_PAGE_SIZE = 256;
- u8 command[] = {
- FLASH_PAGE_PROGRAM,
- (address & 0xff0000) >> 16,
- (address & 0xff00) >> 8,
- 0x00,
- };
- struct wcxb_spi_transfer t_cmd = {
- .tx_buf = command,
- .len = sizeof(command),
- };
- struct wcxb_spi_transfer t_data = {
- .tx_buf = data,
- .len = len,
- };
- struct wcxb_spi_message m;
- /* We need to write on page size boundaries */
- WARN_ON(address & 0xff);
- wcxb_spi_message_init(&m);
- wcxb_spi_message_add_tail(&t_cmd, &m);
- wcxb_spi_message_add_tail(&t_data, &m);
- while (len) {
- res = wcxb_flash_write_enable(spi);
- if (res)
- return res;
- command[1] = (address >> 16) & 0xff;
- command[2] = (address >> 8) & 0xff;
- t_data.tx_buf = data;
- t_data.len = min(len, FLASH_PAGE_SIZE);
- res = wcxb_spi_sync(spi, &m);
- WARN_ON(res);
- if (res)
- return res;
- res = wcxb_flash_wait_until_not_busy(spi);
- WARN_ON(res);
- if (res)
- return res;
- len -= t_data.len;
- address += t_data.len;
- data += t_data.len;
- }
- return 0;
- }
|